Sync update: 2026-05-18 14:22:09
This commit is contained in:
@@ -1,8 +1,8 @@
|
|||||||
class AppConfig {
|
class AppConfig {
|
||||||
static const String serverHost = "mywhatsappapp.interlap.com";
|
static const String serverHost = "mywhatsapp.intaleqapp.com";
|
||||||
static const int serverPort = 3025;
|
static const int serverPort = 3025;
|
||||||
static const String wsUrl = "ws://$serverHost:$serverPort";
|
static const String wsUrl = "ws://$serverHost:$serverPort";
|
||||||
|
|
||||||
static const int maxReconnectAttempts = 10;
|
static const int maxReconnectAttempts = 10;
|
||||||
static const Duration reconnectDelay = Duration(seconds: 3);
|
static const Duration reconnectDelay = Duration(seconds: 3);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ class ChatController extends GetxController {
|
|||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
super.onInit();
|
super.onInit();
|
||||||
|
_svc.activeChatId.value = conversation.id;
|
||||||
|
|
||||||
loadMessages();
|
loadMessages();
|
||||||
markAsRead();
|
markAsRead();
|
||||||
|
|
||||||
@@ -33,6 +35,9 @@ class ChatController extends GetxController {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void onClose() {
|
void onClose() {
|
||||||
|
if (_svc.activeChatId.value == conversation.id) {
|
||||||
|
_svc.activeChatId.value = null;
|
||||||
|
}
|
||||||
_eventSub?.cancel();
|
_eventSub?.cancel();
|
||||||
inputCtrl.dispose();
|
inputCtrl.dispose();
|
||||||
scrollCtrl.dispose();
|
scrollCtrl.dispose();
|
||||||
|
|||||||
@@ -1,19 +1,31 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
import 'package:firebase_core/firebase_core.dart';
|
||||||
import 'services/whatsapp_service.dart';
|
import 'services/whatsapp_service.dart';
|
||||||
|
import 'services/firebase_service.dart';
|
||||||
import 'screens/conversations_screen.dart';
|
import 'screens/conversations_screen.dart';
|
||||||
import 'theme/app_theme.dart';
|
import 'theme/app_theme.dart';
|
||||||
|
|
||||||
void main() {
|
void main() async {
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
|
// Initialize Firebase (Requires flutterfire configure)
|
||||||
|
try {
|
||||||
|
await Firebase.initializeApp();
|
||||||
|
} catch (e) {
|
||||||
|
print('Firebase initialization error: $e');
|
||||||
|
}
|
||||||
|
|
||||||
SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
|
SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
|
||||||
statusBarColor: Colors.transparent,
|
statusBarColor: Colors.transparent,
|
||||||
statusBarIconBrightness: Brightness.light,
|
statusBarIconBrightness: Brightness.light,
|
||||||
));
|
));
|
||||||
|
|
||||||
// Register the WhatsApp WebSocket client service before app starts
|
// Register services before app starts
|
||||||
Get.put(WhatsAppService(), permanent: true);
|
Get.put(WhatsAppService(), permanent: true);
|
||||||
|
Get.put(FirebaseService(), permanent: true);
|
||||||
|
Get.find<FirebaseService>().init();
|
||||||
|
|
||||||
runApp(const WhatsAppApp());
|
runApp(const WhatsAppApp());
|
||||||
}
|
}
|
||||||
|
|||||||
120
whatsapp_app/lib/services/firebase_service.dart
Normal file
120
whatsapp_app/lib/services/firebase_service.dart
Normal 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,6 +11,7 @@ class WhatsAppService extends GetxService {
|
|||||||
final status = WsStatus.disconnected.obs;
|
final status = WsStatus.disconnected.obs;
|
||||||
final qrData = Rx<String?>(null);
|
final qrData = Rx<String?>(null);
|
||||||
final isWaReady = false.obs;
|
final isWaReady = false.obs;
|
||||||
|
final activeChatId = Rx<String?>(null);
|
||||||
|
|
||||||
// ── Internal ─────────────────────────────────────────────────────────────
|
// ── Internal ─────────────────────────────────────────────────────────────
|
||||||
WebSocketChannel? _channel;
|
WebSocketChannel? _channel;
|
||||||
|
|||||||
@@ -1,6 +1,14 @@
|
|||||||
# Generated by pub
|
# Generated by pub
|
||||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||||
packages:
|
packages:
|
||||||
|
_flutterfire_internals:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: _flutterfire_internals
|
||||||
|
sha256: "37a42d06068e2fe3deddb2da079a8c4d105f241225ba27b7122b37e9865fd8f7"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.3.35"
|
||||||
args:
|
args:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -29,10 +37,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: cached_network_image
|
name: cached_network_image
|
||||||
sha256: "7c1183e361e5c8b0a0f21a28401eecdbde252441106a9816400dd4c2b2424916"
|
sha256: "4a5d8d2c728b0f3d0245f69f921d7be90cae4c2fd5288f773088672c0893f819"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.4.1"
|
version: "3.4.0"
|
||||||
cached_network_image_platform_interface:
|
cached_network_image_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -45,10 +53,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: cached_network_image_web
|
name: cached_network_image_web
|
||||||
sha256: "980842f4e8e2535b8dbd3d5ca0b1f0ba66bf61d14cc3a17a9b4788a3685ba062"
|
sha256: "6322dde7a5ad92202e64df659241104a43db20ed594c41ca18de1014598d7996"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.1"
|
version: "1.3.0"
|
||||||
characters:
|
characters:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -97,6 +105,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.9"
|
version: "1.0.9"
|
||||||
|
dbus:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: dbus
|
||||||
|
sha256: d0c98dcd4f5169878b6cf8f6e0a52403a9dff371a3e2f019697accbf6f44a270
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.7.12"
|
||||||
fake_async:
|
fake_async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -121,6 +137,54 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "7.0.1"
|
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:
|
fixnum:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -150,6 +214,30 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.2"
|
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:
|
flutter_test:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description: flutter
|
description: flutter
|
||||||
@@ -376,6 +464,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.0"
|
version: "2.3.0"
|
||||||
|
petitparser:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: petitparser
|
||||||
|
sha256: "91bd59303e9f769f108f8df05e371341b15d59e995e6806aefab827b58336675"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "7.0.2"
|
||||||
platform:
|
platform:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -581,6 +677,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.7.1"
|
version: "3.7.1"
|
||||||
|
timezone:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: timezone
|
||||||
|
sha256: "2236ec079a174ce07434e89fcd3fcda430025eb7692244139a9cf54fdcf1fc7d"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.9.4"
|
||||||
typed_data:
|
typed_data:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -617,10 +721,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: web
|
name: web
|
||||||
sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a"
|
sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.1"
|
version: "0.5.1"
|
||||||
web_socket_channel:
|
web_socket_channel:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -637,6 +741,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.0"
|
version: "1.1.0"
|
||||||
|
xml:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: xml
|
||||||
|
sha256: "971043b3a0d3da28727e40ed3e0b5d18b742fa5a68665cca88e74b7876d5e025"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "6.6.1"
|
||||||
yaml:
|
yaml:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|||||||
@@ -17,6 +17,9 @@ dependencies:
|
|||||||
intl: ^0.19.0
|
intl: ^0.19.0
|
||||||
timeago: ^3.6.1
|
timeago: ^3.6.1
|
||||||
shared_preferences: ^2.2.2
|
shared_preferences: ^2.2.2
|
||||||
|
firebase_core: ^2.31.1
|
||||||
|
firebase_messaging: ^14.9.1
|
||||||
|
flutter_local_notifications: ^17.1.2
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|||||||
@@ -124,6 +124,7 @@ function initWhatsApp() {
|
|||||||
waClient = new Client({
|
waClient = new Client({
|
||||||
authStrategy: new LocalAuth({ clientId: 'whatsapp-bridge' }),
|
authStrategy: new LocalAuth({ clientId: 'whatsapp-bridge' }),
|
||||||
puppeteer: {
|
puppeteer: {
|
||||||
|
executablePath: process.env.CHROME_BIN || '/usr/bin/chromium',
|
||||||
headless: 'new',
|
headless: 'new',
|
||||||
args: [
|
args: [
|
||||||
'--no-sandbox',
|
'--no-sandbox',
|
||||||
|
|||||||
Reference in New Issue
Block a user