147 lines
4.0 KiB
Dart
147 lines
4.0 KiB
Dart
import 'dart:async';
|
|
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
|
|
import '../../core/storage/app_preferences.dart';
|
|
import '../models/reminder_payload.dart';
|
|
|
|
typedef ReminderNotificationResponseHandler =
|
|
Future<void> Function(NotificationResponse response);
|
|
|
|
class ReminderNotificationCallbacks {
|
|
static ReminderNotificationResponseHandler? _responseHandler;
|
|
static Future<void> _pendingStorageLock = Future<void>.value();
|
|
static void Function(ReminderPayload)? onNotificationPayloadReceived;
|
|
static AppPreferences? _appPreferences;
|
|
|
|
static Future<AppPreferences> _resolvePrefs() async {
|
|
final prefs = _appPreferences;
|
|
if (prefs != null) {
|
|
return prefs;
|
|
}
|
|
final sharedPreferences = await SharedPreferences.getInstance();
|
|
final fallback = AppPreferences(sharedPreferences);
|
|
_appPreferences = fallback;
|
|
return fallback;
|
|
}
|
|
|
|
static void setAppPreferences(AppPreferences prefs) {
|
|
_appPreferences = prefs;
|
|
}
|
|
|
|
@visibleForTesting
|
|
static Future<void> resetForTest() async {
|
|
_responseHandler = null;
|
|
_pendingStorageLock = Future<void>.value();
|
|
final prefs = await _resolvePrefs();
|
|
await prefs.clearPendingNotifications();
|
|
}
|
|
|
|
static Future<void> bindResponseHandler(
|
|
ReminderNotificationResponseHandler handler,
|
|
) async {
|
|
_responseHandler = handler;
|
|
await _drainPendingResponses();
|
|
}
|
|
|
|
static Future<void> onForegroundResponse(
|
|
NotificationResponse response,
|
|
) async {
|
|
final handler = _responseHandler;
|
|
if (handler == null) {
|
|
await _enqueuePendingResponse(response);
|
|
return;
|
|
}
|
|
try {
|
|
await handler(response);
|
|
} catch (_) {
|
|
await _enqueuePendingResponse(response);
|
|
}
|
|
}
|
|
|
|
static Future<void> onBackgroundResponse(
|
|
NotificationResponse response,
|
|
) async {
|
|
final handler = _responseHandler;
|
|
if (handler == null) {
|
|
await _enqueuePendingResponse(response);
|
|
return;
|
|
}
|
|
try {
|
|
await handler(response);
|
|
} catch (_) {
|
|
await _enqueuePendingResponse(response);
|
|
}
|
|
}
|
|
|
|
static Future<T> _withPendingStorageLock<T>(Future<T> Function() operation) {
|
|
final completer = Completer<void>();
|
|
final waitForTurn = _pendingStorageLock;
|
|
_pendingStorageLock = waitForTurn.then((_) => completer.future);
|
|
|
|
return waitForTurn.then((_) => operation()).whenComplete(() {
|
|
if (!completer.isCompleted) {
|
|
completer.complete();
|
|
}
|
|
});
|
|
}
|
|
|
|
static Future<void> _enqueuePendingResponse(
|
|
NotificationResponse response,
|
|
) async {
|
|
final prefs = await _resolvePrefs();
|
|
await _withPendingStorageLock(() async {
|
|
final current = prefs.pendingNotifications;
|
|
await prefs.setPendingNotifications([
|
|
...current,
|
|
NotificationResponse(
|
|
id: response.id,
|
|
actionId: response.actionId,
|
|
payload: response.payload,
|
|
input: response.input,
|
|
notificationResponseType: response.notificationResponseType,
|
|
),
|
|
]);
|
|
});
|
|
}
|
|
|
|
static Future<void> _drainPendingResponses() async {
|
|
final handler = _responseHandler;
|
|
if (handler == null) {
|
|
return;
|
|
}
|
|
final prefs = await _resolvePrefs();
|
|
await _withPendingStorageLock(() async {
|
|
final pending = prefs.pendingNotifications;
|
|
if (pending.isEmpty) {
|
|
return;
|
|
}
|
|
|
|
final remaining = <NotificationResponse>[];
|
|
for (final response in pending) {
|
|
try {
|
|
await handler(response);
|
|
} catch (_) {
|
|
remaining.add(response);
|
|
}
|
|
}
|
|
|
|
if (remaining.isEmpty) {
|
|
await prefs.clearPendingNotifications();
|
|
return;
|
|
}
|
|
|
|
await prefs.setPendingNotifications(remaining);
|
|
});
|
|
}
|
|
}
|
|
|
|
@pragma('vm:entry-point')
|
|
Future<void> reminderNotificationTapBackground(
|
|
NotificationResponse response,
|
|
) async {
|
|
await ReminderNotificationCallbacks.onBackgroundResponse(response);
|
|
}
|