// lib/services/api/push_notification_service.dart import 'dart:async'; import 'package:Stocky/Pages/controllers/MainController.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; import 'api_service.dart'; // Убедитесь, что путь правильный class PushNotificationService extends GetxService { final FirebaseMessaging _messaging = FirebaseMessaging.instance; final StreamController _tokenController = StreamController.broadcast(); final StreamController _foregroundMessageController = StreamController.broadcast(); // Поток для уведомлений, пришедших при открытом приложении Stream get foregroundMessageStream => _foregroundMessageController.stream; // Поток FCM-токена Stream get tokenStream => _tokenController.stream; @override void onInit() { _initFirebase(); super.onInit(); } @override void onClose() { _tokenController.close(); _foregroundMessageController.close(); super.onClose(); } Future _initFirebase() async { try { await Firebase.initializeApp(); // Запрашиваем разрешение await _requestPermission(); // Получаем токен await _getToken(); // Настраиваем обработчики _setupMessageHandling(); } catch (e) { debugPrint('Ошибка инициализации FCM: $e'); } } Future _requestPermission() async { final status = await _messaging.requestPermission( alert: true, badge: true, sound: true, // остальные по умолчанию false ); if (status.authorizationStatus == AuthorizationStatus.authorized || status.authorizationStatus == AuthorizationStatus.provisional) { debugPrint('Разрешение на уведомления получено'); } else { debugPrint('Разрешение отклонено'); } } Future _getToken() async { try { final String? token = await _messaging.getToken(); if (token != null) { _tokenController.add(token); print('FCM Token: $token'); // Отправляем токен на сервер — но только если ApiService уже инициализирован if (Get.isRegistered()) { Get.find().setFcmToken(token); } else { // Опционально: сохраните токен локально и отправьте позже print('ApiService не готов — токен временно не отправлен'); } } } catch (e) { debugPrint('Ошибка получения токена FCM: $e'); } } void _setupMessageHandling() { // Уведомления, пришедшие при открытом приложении FirebaseMessaging.onMessage.listen((RemoteMessage message) { debugPrint('Уведомление в foreground: ${message.notification?.title}'); Get.find().fetchTasks(); _foregroundMessageController.add(message); // ← Передаём дальше }); // Пользователь нажал на уведомление из фонового режима FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) { debugPrint('Уведомление открыто из фона: ${message.notification?.title}'); _handleMessageAction(message); }); // Фоновый обработчик (для закрытого приложения) FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler); } // Обработка действия из уведомления void _handleMessageAction(RemoteMessage message) { final data = message.data; // Пример: действие зависит от поля "action" в payload final action = data['action']; switch (action) { case 'open_task': final taskId = data['task_id']; if (taskId != null) { // Здесь можно вызвать навигацию или обновить состояние // Например, через Get.toNamed('/task/$taskId'); // Но лучше делегировать это MainController или другому слушателю Get.find().openTaskFromNotification(taskId); } break; case 'refresh_tasks': if (Get.isRegistered()) { Get.find().fetchTasks(); } break; // Добавьте другие действия по мере необходимости default: debugPrint('Неизвестное действие: $action'); } } // Статический фоновый обработчик (ограниченный функционал) static Future _firebaseMessagingBackgroundHandler( RemoteMessage message, ) async { // В фоне нельзя использовать GetX, Dio с авторизацией и т.п. // Только простые операции: логирование, запись в Hive и т.д. debugPrint('Фоновое уведомление: ${message.messageId}'); // Пример: сохранить в локальную БД, чтобы обработать при запуске } }