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;

View File

@@ -1,6 +1,14 @@
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
_flutterfire_internals:
dependency: transitive
description:
name: _flutterfire_internals
sha256: "37a42d06068e2fe3deddb2da079a8c4d105f241225ba27b7122b37e9865fd8f7"
url: "https://pub.dev"
source: hosted
version: "1.3.35"
args:
dependency: transitive
description:
@@ -29,10 +37,10 @@ packages:
dependency: "direct main"
description:
name: cached_network_image
sha256: "7c1183e361e5c8b0a0f21a28401eecdbde252441106a9816400dd4c2b2424916"
sha256: "4a5d8d2c728b0f3d0245f69f921d7be90cae4c2fd5288f773088672c0893f819"
url: "https://pub.dev"
source: hosted
version: "3.4.1"
version: "3.4.0"
cached_network_image_platform_interface:
dependency: transitive
description:
@@ -45,10 +53,10 @@ packages:
dependency: transitive
description:
name: cached_network_image_web
sha256: "980842f4e8e2535b8dbd3d5ca0b1f0ba66bf61d14cc3a17a9b4788a3685ba062"
sha256: "6322dde7a5ad92202e64df659241104a43db20ed594c41ca18de1014598d7996"
url: "https://pub.dev"
source: hosted
version: "1.3.1"
version: "1.3.0"
characters:
dependency: transitive
description:
@@ -97,6 +105,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.9"
dbus:
dependency: transitive
description:
name: dbus
sha256: d0c98dcd4f5169878b6cf8f6e0a52403a9dff371a3e2f019697accbf6f44a270
url: "https://pub.dev"
source: hosted
version: "0.7.12"
fake_async:
dependency: transitive
description:
@@ -121,6 +137,54 @@ packages:
url: "https://pub.dev"
source: hosted
version: "7.0.1"
firebase_core:
dependency: "direct main"
description:
name: firebase_core
sha256: "26de145bb9688a90962faec6f838247377b0b0d32cc0abecd9a4e43525fc856c"
url: "https://pub.dev"
source: hosted
version: "2.32.0"
firebase_core_platform_interface:
dependency: transitive
description:
name: firebase_core_platform_interface
sha256: "8bcfad6d7033f5ea951d15b867622a824b13812178bfec0c779b9d81de011bbb"
url: "https://pub.dev"
source: hosted
version: "5.4.2"
firebase_core_web:
dependency: transitive
description:
name: firebase_core_web
sha256: "362e52457ed2b7b180964769c1e04d1e0ea0259fdf7025fdfedd019d4ae2bd88"
url: "https://pub.dev"
source: hosted
version: "2.17.5"
firebase_messaging:
dependency: "direct main"
description:
name: firebase_messaging
sha256: a1662cc95d9750a324ad9df349b873360af6f11414902021f130c68ec02267c4
url: "https://pub.dev"
source: hosted
version: "14.9.4"
firebase_messaging_platform_interface:
dependency: transitive
description:
name: firebase_messaging_platform_interface
sha256: "87c4a922cb6f811cfb7a889bdbb3622702443c52a0271636cbc90d813ceac147"
url: "https://pub.dev"
source: hosted
version: "4.5.37"
firebase_messaging_web:
dependency: transitive
description:
name: firebase_messaging_web
sha256: "0d34dca01a7b103ed7f20138bffbb28eb0e61a677bf9e78a028a932e2c7322d5"
url: "https://pub.dev"
source: hosted
version: "3.8.7"
fixnum:
dependency: transitive
description:
@@ -150,6 +214,30 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.0.2"
flutter_local_notifications:
dependency: "direct main"
description:
name: flutter_local_notifications
sha256: "674173fd3c9eda9d4c8528da2ce0ea69f161577495a9cc835a2a4ecd7eadeb35"
url: "https://pub.dev"
source: hosted
version: "17.2.4"
flutter_local_notifications_linux:
dependency: transitive
description:
name: flutter_local_notifications_linux
sha256: c49bd06165cad9beeb79090b18cd1eb0296f4bf4b23b84426e37dd7c027fc3af
url: "https://pub.dev"
source: hosted
version: "4.0.1"
flutter_local_notifications_platform_interface:
dependency: transitive
description:
name: flutter_local_notifications_platform_interface
sha256: "85f8d07fe708c1bdcf45037f2c0109753b26ae077e9d9e899d55971711a4ea66"
url: "https://pub.dev"
source: hosted
version: "7.2.0"
flutter_test:
dependency: "direct dev"
description: flutter
@@ -376,6 +464,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.3.0"
petitparser:
dependency: transitive
description:
name: petitparser
sha256: "91bd59303e9f769f108f8df05e371341b15d59e995e6806aefab827b58336675"
url: "https://pub.dev"
source: hosted
version: "7.0.2"
platform:
dependency: transitive
description:
@@ -581,6 +677,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.7.1"
timezone:
dependency: transitive
description:
name: timezone
sha256: "2236ec079a174ce07434e89fcd3fcda430025eb7692244139a9cf54fdcf1fc7d"
url: "https://pub.dev"
source: hosted
version: "0.9.4"
typed_data:
dependency: transitive
description:
@@ -617,10 +721,10 @@ packages:
dependency: transitive
description:
name: web
sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a"
sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27"
url: "https://pub.dev"
source: hosted
version: "1.1.1"
version: "0.5.1"
web_socket_channel:
dependency: "direct main"
description:
@@ -637,6 +741,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.1.0"
xml:
dependency: transitive
description:
name: xml
sha256: "971043b3a0d3da28727e40ed3e0b5d18b742fa5a68665cca88e74b7876d5e025"
url: "https://pub.dev"
source: hosted
version: "6.6.1"
yaml:
dependency: transitive
description:

View File

@@ -17,6 +17,9 @@ dependencies:
intl: ^0.19.0
timeago: ^3.6.1
shared_preferences: ^2.2.2
firebase_core: ^2.31.1
firebase_messaging: ^14.9.1
flutter_local_notifications: ^17.1.2
dev_dependencies:
flutter_test: