import 'dart:developer'; import 'package:eraser/eraser.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/services.dart'; import 'package:flutter_app_badge/flutter_app_badge.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import '../routing/app_routes.dart'; import '../state/app/modules/chat_list/bloc/chat_list_bloc.dart'; import 'notification_service.dart'; class NotificationTasks { static void updateBadgeCount(RemoteMessage notification) { FlutterAppBadge.count( int.parse((notification.data['unreadCount'] as String?) ?? '0'), ); } /// Per-chat tag scheme. MUST match the Notify backend, which sets this /// value on `AndroidNotification.setTag` AND `apns-collapse-id`. static String chatTag(String chatToken) => 'talk_$chatToken'; /// Removes tray notifications belonging to [chatToken]. Eraser handles /// iOS (where the plugin's `getActiveNotifications` returns null ids /// for FCM posts and can't cancel them); the local-notifications sweep /// handles Android and acts as a fallback while Eraser's native side /// isn't built in yet. static Future clearNotificationsForChat(String chatToken) async { final tag = chatTag(chatToken); try { await Eraser.clearAppNotificationsByTag(tag); } on MissingPluginException { // Eraser native code not yet linked — needs flutter clean + run. } on Object catch (e) { log('Eraser($tag) failed: $e'); } try { final plugin = NotificationService().flutterLocalNotificationsPlugin; final actives = await plugin.getActiveNotifications(); for (final n in actives) { final id = n.id; if (id == null) continue; if (n.tag == tag) await plugin.cancel(id: id, tag: n.tag); } } on Object catch (e) { log('Active-notification sweep failed: $e'); } } /// Refreshes the chat list. Deliberately does NOT touch [ChatBloc] — /// the open chat view manages its own state via long-poll, and refreshing /// it here would re-fetch the last-opened chat with setReadMarker=on /// even if the user has already left. static void updateProviders(BuildContext context) { context.read().refresh(); } /// Switches to the Talk tab. If [chatToken] is provided, also schedules /// the matching chat to be opened automatically once the chat list view /// resolves the token (handled inside [ChatList]). static void navigateToTalk(BuildContext context, {String? chatToken}) { if (chatToken != null && chatToken.isNotEmpty) { AppRoutes.openChatByToken(context, chatToken); } else { AppRoutes.goToTalkTab(context); } } }