Sync update: 2026-05-18 14:22:09
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
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 qrData = Rx<String?>(null);
|
||||
final isWaReady = false.obs;
|
||||
final activeChatId = Rx<String?>(null);
|
||||
|
||||
// ── Internal ─────────────────────────────────────────────────────────────
|
||||
WebSocketChannel? _channel;
|
||||
|
||||
Reference in New Issue
Block a user