Sync update: 2026-05-18 14:22:09

This commit is contained in:
Hamza-Ayed
2026-05-18 14:22:09 +03:00
parent a60a173b51
commit 30d32df1c0
8 changed files with 264 additions and 10 deletions

View File

@@ -1,8 +1,8 @@
class AppConfig {
static const String serverHost = "mywhatsappapp.interlap.com";
static const String serverHost = "mywhatsapp.intaleqapp.com";
static const int serverPort = 3025;
static const String wsUrl = "ws://$serverHost:$serverPort";
static const int maxReconnectAttempts = 10;
static const Duration reconnectDelay = Duration(seconds: 3);
}

View File

@@ -24,6 +24,8 @@ class ChatController extends GetxController {
@override
void onInit() {
super.onInit();
_svc.activeChatId.value = conversation.id;
loadMessages();
markAsRead();
@@ -33,6 +35,9 @@ class ChatController extends GetxController {
@override
void onClose() {
if (_svc.activeChatId.value == conversation.id) {
_svc.activeChatId.value = null;
}
_eventSub?.cancel();
inputCtrl.dispose();
scrollCtrl.dispose();

View File

@@ -1,19 +1,31 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:firebase_core/firebase_core.dart';
import 'services/whatsapp_service.dart';
import 'services/firebase_service.dart';
import 'screens/conversations_screen.dart';
import 'theme/app_theme.dart';
void main() {
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Initialize Firebase (Requires flutterfire configure)
try {
await Firebase.initializeApp();
} catch (e) {
print('Firebase initialization error: $e');
}
SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
statusBarIconBrightness: Brightness.light,
));
// Register the WhatsApp WebSocket client service before app starts
// Register services before app starts
Get.put(WhatsAppService(), permanent: true);
Get.put(FirebaseService(), permanent: true);
Get.find<FirebaseService>().init();
runApp(const WhatsAppApp());
}

View File

@@ -0,0 +1,120 @@
import 'dart:convert';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:get/get.dart';
import '../models/conversation_model.dart';
import '../screens/chat_screen.dart';
import 'whatsapp_service.dart';
@pragma('vm:entry-point')
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
// We can't access GetX services easily here since it's a separate isolate,
// but Firebase automatically shows the notification.
print("Handling a background message: ${message.messageId}");
}
class FirebaseService extends GetxService {
final FirebaseMessaging _messaging = FirebaseMessaging.instance;
final FlutterLocalNotificationsPlugin _localNotifications = FlutterLocalNotificationsPlugin();
Future<FirebaseService> init() async {
// 1. Request Permission
await _messaging.requestPermission(
alert: true,
badge: true,
sound: true,
);
// 2. Initialize Local Notifications (for foreground)
const androidInit = AndroidInitializationSettings('@mipmap/ic_launcher');
const iosInit = DarwinInitializationSettings();
const initSettings = InitializationSettings(android: androidInit, iOS: iosInit);
await _localNotifications.initialize(
initSettings,
onDidReceiveNotificationResponse: _onNotificationTap,
);
// 3. Background Handler
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
// 4. Foreground Message Handler
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
final chatId = message.data['chatId'];
final activeChatId = Get.find<WhatsAppService>().activeChatId.value;
// Smart Notification: Only show if we are NOT currently in this chat
if (chatId != null && activeChatId == chatId) {
print('[FCM] Silent notification, user is currently in chat $chatId');
return; // Silent
}
_showLocalNotification(message);
});
// 5. Handle tapping on a background notification when app opens
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
_handleNotificationClick(message.data);
});
// 6. Check if app was opened from a terminated state via a notification
final initialMessage = await _messaging.getInitialMessage();
if (initialMessage != null) {
Future.delayed(const Duration(seconds: 1), () {
_handleNotificationClick(initialMessage.data);
});
}
return this;
}
void _showLocalNotification(RemoteMessage message) {
final notification = message.notification;
if (notification == null) return;
const androidDetails = AndroidNotificationDetails(
'whatsapp_channel',
'WhatsApp Messages',
importance: Importance.max,
priority: Priority.high,
ticker: 'ticker',
);
const iosDetails = DarwinNotificationDetails();
const details = NotificationDetails(android: androidDetails, iOS: iosDetails);
_localNotifications.show(
notification.hashCode,
notification.title,
notification.body,
details,
payload: jsonEncode(message.data),
);
}
void _onNotificationTap(NotificationResponse response) {
if (response.payload != null) {
final data = jsonDecode(response.payload!);
_handleNotificationClick(data);
}
}
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(
id: chatId,
name: name,
isGroup: false,
unreadCount: 0,
timestamp: 0,
pinned: false,
isMuted: false,
);
Get.to(() => ChatScreen(conversation: dummyChat));
}
}
}

View File

@@ -11,6 +11,7 @@ class WhatsAppService extends GetxService {
final status = WsStatus.disconnected.obs;
final qrData = Rx<String?>(null);
final isWaReady = false.obs;
final activeChatId = Rx<String?>(null);
// ── Internal ─────────────────────────────────────────────────────────────
WebSocketChannel? _channel;