Files
social-app/apps/lib/data/services/reminder_notification_callbacks.dart
T

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);
}