Sync update: 2026-05-18 16:14:25
This commit is contained in:
85
whatsapp_app/lib/services/contacts_service.dart
Normal file
85
whatsapp_app/lib/services/contacts_service.dart
Normal file
@@ -0,0 +1,85 @@
|
||||
import 'package:flutter_contacts/flutter_contacts.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class ContactInfo {
|
||||
final String name;
|
||||
final String? avatarPath;
|
||||
ContactInfo({required this.name, this.avatarPath});
|
||||
}
|
||||
|
||||
class ContactsService extends GetxService {
|
||||
final RxMap<String, ContactInfo> _contactsMap = <String, ContactInfo>{}.obs;
|
||||
final RxBool permissionGranted = false.obs;
|
||||
|
||||
Future<ContactsService> init() async {
|
||||
await fetchContacts();
|
||||
return this;
|
||||
}
|
||||
|
||||
Future<void> fetchContacts() async {
|
||||
try {
|
||||
// Check and request permission
|
||||
bool permission = await FlutterContacts.requestPermission(readonly: true);
|
||||
permissionGranted.value = permission;
|
||||
|
||||
if (permission) {
|
||||
// Fetch contacts with photos and phone numbers
|
||||
final contacts = await FlutterContacts.getContacts(withProperties: true, withPhoto: true);
|
||||
|
||||
final Map<String, ContactInfo> tempMap = {};
|
||||
for (var contact in contacts) {
|
||||
final fullName = contact.displayName;
|
||||
if (fullName.isEmpty) continue;
|
||||
|
||||
for (var phone in contact.phones) {
|
||||
final normalized = normalizePhoneNumber(phone.number);
|
||||
if (normalized.isNotEmpty) {
|
||||
tempMap[normalized] = ContactInfo(
|
||||
name: fullName,
|
||||
avatarPath: null, // Custom local avatar path can be handled if needed
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
_contactsMap.assignAll(tempMap);
|
||||
print('[CONTACTS] Successfully loaded ${_contactsMap.length} normalized phone contacts');
|
||||
}
|
||||
} catch (e) {
|
||||
print('[CONTACTS ERROR] Failed to fetch system contacts: $e');
|
||||
}
|
||||
}
|
||||
|
||||
// Normalizes numbers to match them easily (e.g., removes spaces, dashes, brackets, and leading zeros)
|
||||
String normalizePhoneNumber(String number) {
|
||||
String clean = number.replaceAll(RegExp(r'[\s\-\(\)\+]'), '');
|
||||
// If it starts with local country prefix or leading 0, we can do substring matches
|
||||
if (clean.startsWith('00')) {
|
||||
clean = clean.substring(2);
|
||||
}
|
||||
return clean;
|
||||
}
|
||||
|
||||
String getContactName(String rawNumber, String fallback) {
|
||||
if (!permissionGranted.value || _contactsMap.isEmpty) return fallback;
|
||||
|
||||
final clean = normalizePhoneNumber(rawNumber);
|
||||
if (clean.isEmpty) return fallback;
|
||||
|
||||
// Direct match
|
||||
if (_contactsMap.containsKey(clean)) {
|
||||
return _contactsMap[clean]!.name;
|
||||
}
|
||||
|
||||
// Partial match for varying country codes (match last 9 digits of the phone number)
|
||||
if (clean.length >= 9) {
|
||||
final suffix = clean.substring(clean.length - 9);
|
||||
for (var key in _contactsMap.keys) {
|
||||
if (key.endsWith(suffix)) {
|
||||
return _contactsMap[key]!.name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return fallback;
|
||||
}
|
||||
}
|
||||
@@ -215,6 +215,28 @@ class WhatsAppService extends GetxService {
|
||||
Future<Map<String, dynamic>> sendFcmToken(String token) =>
|
||||
_request({ 'type': 'register_fcm', 'token': token });
|
||||
|
||||
Future<Map<String, dynamic>> getMedia(String messageId) =>
|
||||
_request({ 'type': 'get_media', 'messageId': messageId });
|
||||
|
||||
// Cache downloaded media: messageId -> base64
|
||||
final RxMap<String, String> mediaCache = <String, String>{}.obs;
|
||||
|
||||
Future<String?> downloadAndCacheMedia(String messageId) async {
|
||||
if (mediaCache.containsKey(messageId)) return mediaCache[messageId];
|
||||
|
||||
try {
|
||||
final res = await getMedia(messageId);
|
||||
if (res['type'] == 'media' && res['data'] != null) {
|
||||
final String base64Data = res['data'];
|
||||
mediaCache[messageId] = base64Data;
|
||||
return base64Data;
|
||||
}
|
||||
} catch (e) {
|
||||
print('[MEDIA DOWNLOAD ERROR] Failed to download message media: $e');
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> ping() =>
|
||||
_request({ 'type': 'ping' });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user