implemented an E2E-encrypted Nextcloud push-v2 notification system with support for RSA decryption and signature verification; introduced an iOS Notification Service Extension and native AppDelegate handlers for Talk actions (inline reply and mark-as-read); replaced the legacy notification registration with a new lifecycle managing app passwords and secure keypair storage; added background message handling with tray synchronization and a test notification utility in the settings.
This commit is contained in:
@@ -4,44 +4,36 @@ import 'package:firebase_messaging/firebase_messaging.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import '../push/push_message_handler.dart';
|
||||
import '../state/app/modules/chat/bloc/chat_bloc.dart';
|
||||
import '../widget/debug/debug_tile.dart';
|
||||
import '../widget/debug/json_viewer.dart';
|
||||
import '../widget/info_dialog.dart';
|
||||
import 'notification_tasks.dart';
|
||||
|
||||
// `vm:entry-point` keeps this alive through AOT tree-shaking — the FCM
|
||||
// background isolate looks the class up by name from native code.
|
||||
@pragma('vm:entry-point')
|
||||
/// Bridges FCM lifecycle callbacks to the push pipeline. Background messages are
|
||||
/// handled directly by [PushMessageHandler.onBackgroundMessage]; this class
|
||||
/// covers the foreground and app-opened paths where a [BuildContext] is
|
||||
/// available.
|
||||
class NotificationController {
|
||||
@pragma('vm:entry-point')
|
||||
static Future<void> onBackgroundMessageHandler(RemoteMessage message) async {
|
||||
NotificationTasks.updateBadgeCount(message);
|
||||
}
|
||||
|
||||
static Future<void> onForegroundMessageHandler(
|
||||
RemoteMessage message,
|
||||
BuildContext context,
|
||||
) async {
|
||||
final pushToken = _extractChatToken(message);
|
||||
final chatBloc = context.read<ChatBloc>();
|
||||
// hasOpenChat, not currentToken: currentToken sticks around after
|
||||
// leaveChat so didPopNext can re-claim a stacked chat.
|
||||
final activeToken = chatBloc.state.data?.currentToken ?? '';
|
||||
final chatIsOpen =
|
||||
chatBloc.hasOpenChat &&
|
||||
pushToken != null &&
|
||||
pushToken.isNotEmpty &&
|
||||
pushToken == activeToken;
|
||||
|
||||
NotificationTasks.updateBadgeCount(message);
|
||||
|
||||
if (chatIsOpen) {
|
||||
// Long-poll handles the message; just dismiss any stray tray entry.
|
||||
unawaited(NotificationTasks.clearNotificationsForChat(pushToken));
|
||||
return;
|
||||
}
|
||||
final openChatToken = chatBloc.hasOpenChat
|
||||
? (chatBloc.state.data?.currentToken ?? '')
|
||||
: null;
|
||||
|
||||
await PushMessageHandler().handle(
|
||||
message,
|
||||
foreground: true,
|
||||
openChatToken: openChatToken,
|
||||
);
|
||||
await NotificationTasks.refreshBadge();
|
||||
if (!context.mounted) return;
|
||||
NotificationTasks.updateProviders(context);
|
||||
}
|
||||
|
||||
@@ -54,6 +46,7 @@ class NotificationController {
|
||||
chatToken: _extractChatToken(message),
|
||||
);
|
||||
NotificationTasks.updateProviders(context);
|
||||
unawaited(NotificationTasks.refreshBadge());
|
||||
|
||||
DebugTile(context).run(() {
|
||||
InfoDialog.show(
|
||||
|
||||
Reference in New Issue
Block a user