Sync update: 2026-05-18 15:45:06
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'package:firebase_messaging/firebase_messaging.dart';
|
||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||
@@ -15,7 +16,8 @@ Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
|
||||
|
||||
class FirebaseService extends GetxService {
|
||||
final FirebaseMessaging _messaging = FirebaseMessaging.instance;
|
||||
final FlutterLocalNotificationsPlugin _localNotifications = FlutterLocalNotificationsPlugin();
|
||||
final FlutterLocalNotificationsPlugin _localNotifications =
|
||||
FlutterLocalNotificationsPlugin();
|
||||
|
||||
Future<FirebaseService> init() async {
|
||||
// 1. Request Permission
|
||||
@@ -28,8 +30,9 @@ class FirebaseService extends GetxService {
|
||||
// 2. Initialize Local Notifications (for foreground)
|
||||
const androidInit = AndroidInitializationSettings('@mipmap/ic_launcher');
|
||||
const iosInit = DarwinInitializationSettings();
|
||||
const initSettings = InitializationSettings(android: androidInit, iOS: iosInit);
|
||||
|
||||
const initSettings =
|
||||
InitializationSettings(android: androidInit, iOS: iosInit);
|
||||
|
||||
await _localNotifications.initialize(
|
||||
initSettings,
|
||||
onDidReceiveNotificationResponse: _onNotificationTap,
|
||||
@@ -65,9 +68,48 @@ class FirebaseService extends GetxService {
|
||||
});
|
||||
}
|
||||
|
||||
// 7. Request and register FCM Token on the server
|
||||
_setupFcmTokenRegistration();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
void _setupFcmTokenRegistration() async {
|
||||
try {
|
||||
final token = await _messaging.getToken();
|
||||
if (token != null) {
|
||||
print('[FCM] Token obtained: ${token.substring(0, 15)}...');
|
||||
_registerTokenOnServer(token);
|
||||
}
|
||||
} catch (e) {
|
||||
print('[FCM ERROR] Could not get token: $e');
|
||||
}
|
||||
|
||||
_messaging.onTokenRefresh.listen((newToken) {
|
||||
print('[FCM] Token refreshed');
|
||||
_registerTokenOnServer(newToken);
|
||||
});
|
||||
}
|
||||
|
||||
void _registerTokenOnServer(String token) {
|
||||
final wsSvc = Get.find<WhatsAppService>();
|
||||
if (wsSvc.status.value == WsStatus.waReady ||
|
||||
wsSvc.status.value == WsStatus.connected) {
|
||||
print('[FCM] Sending token to server...');
|
||||
wsSvc.sendFcmToken(token);
|
||||
} else {
|
||||
// Listen to status changes and send once connected
|
||||
late StreamSubscription sub;
|
||||
sub = wsSvc.status.listen((status) {
|
||||
if (status == WsStatus.waReady || status == WsStatus.connected) {
|
||||
print('[FCM] WebSocket connected, sending token to server...');
|
||||
wsSvc.sendFcmToken(token);
|
||||
sub.cancel();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void _showLocalNotification(RemoteMessage message) {
|
||||
final notification = message.notification;
|
||||
if (notification == null) return;
|
||||
@@ -80,7 +122,8 @@ class FirebaseService extends GetxService {
|
||||
ticker: 'ticker',
|
||||
);
|
||||
const iosDetails = DarwinNotificationDetails();
|
||||
const details = NotificationDetails(android: androidDetails, iOS: iosDetails);
|
||||
const details =
|
||||
NotificationDetails(android: androidDetails, iOS: iosDetails);
|
||||
|
||||
_localNotifications.show(
|
||||
notification.hashCode,
|
||||
@@ -101,7 +144,7 @@ class FirebaseService extends GetxService {
|
||||
void _handleNotificationClick(Map<String, dynamic> data) {
|
||||
final chatId = data['chatId'];
|
||||
final name = data['name'] ?? 'Chat';
|
||||
|
||||
|
||||
if (chatId != null) {
|
||||
// Mock a conversation model to navigate to ChatScreen
|
||||
final dummyChat = ConversationModel(
|
||||
@@ -113,7 +156,7 @@ class FirebaseService extends GetxService {
|
||||
pinned: false,
|
||||
isMuted: false,
|
||||
);
|
||||
|
||||
|
||||
Get.to(() => ChatScreen(conversation: dummyChat));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,6 +51,7 @@ class WhatsAppService extends GetxService {
|
||||
|
||||
status.value = WsStatus.connecting;
|
||||
_reconnectTimer?.cancel();
|
||||
print('[WS] Attempting to connect to ${AppConfig.wsUrl}...');
|
||||
|
||||
try {
|
||||
_channel = WebSocketChannel.connect(Uri.parse(AppConfig.wsUrl));
|
||||
@@ -62,15 +63,18 @@ class WhatsAppService extends GetxService {
|
||||
);
|
||||
status.value = WsStatus.connected;
|
||||
_reconnectCount = 0;
|
||||
print('[WS] Connected successfully.');
|
||||
|
||||
// Request initial status check
|
||||
ping();
|
||||
} catch (e) {
|
||||
print('[WS] Connection exception: $e');
|
||||
_scheduleReconnect();
|
||||
}
|
||||
}
|
||||
|
||||
void _onData(dynamic raw) {
|
||||
print('[WS RECV] $raw');
|
||||
Map<String, dynamic> data;
|
||||
try {
|
||||
data = jsonDecode(raw as String);
|
||||
@@ -127,10 +131,12 @@ class WhatsAppService extends GetxService {
|
||||
}
|
||||
|
||||
void _onError(Object err) {
|
||||
print('[WS ERROR] $err');
|
||||
_handleDisconnect();
|
||||
}
|
||||
|
||||
void _onDone() {
|
||||
print('[WS DONE] Connection closed by server');
|
||||
_handleDisconnect();
|
||||
}
|
||||
|
||||
@@ -169,16 +175,18 @@ class WhatsAppService extends GetxService {
|
||||
_pending[id] = completer;
|
||||
|
||||
try {
|
||||
_channel?.sink.add(jsonEncode(payload));
|
||||
final jsonMsg = jsonEncode(payload);
|
||||
print('[WS SEND] $jsonMsg');
|
||||
_channel?.sink.add(jsonMsg);
|
||||
} catch (e) {
|
||||
_pending.remove(id);
|
||||
completer.completeError(e);
|
||||
return completer.future;
|
||||
}
|
||||
|
||||
// Timeout after 15s
|
||||
// Timeout after 60s
|
||||
return completer.future.timeout(
|
||||
const Duration(seconds: 15),
|
||||
const Duration(seconds: 60),
|
||||
onTimeout: () {
|
||||
_pending.remove(id);
|
||||
throw TimeoutException('Request timed out: ${payload['type']}');
|
||||
@@ -204,6 +212,9 @@ class WhatsAppService extends GetxService {
|
||||
Future<Map<String, dynamic>> searchConversations(String query) =>
|
||||
_request({ 'type': 'search_conversations', 'query': query });
|
||||
|
||||
Future<Map<String, dynamic>> sendFcmToken(String token) =>
|
||||
_request({ 'type': 'register_fcm', 'token': token });
|
||||
|
||||
Future<Map<String, dynamic>> ping() =>
|
||||
_request({ 'type': 'ping' });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user