Sync update: 2026-05-18 15:45:06

This commit is contained in:
Hamza-Ayed
2026-05-18 15:45:07 +03:00
parent 30d32df1c0
commit 82650b8c1f
141 changed files with 5255 additions and 24 deletions

View File

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

View File

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