Sync update: 2026-05-18 16:14:25

This commit is contained in:
Hamza-Ayed
2026-05-18 16:14:25 +03:00
parent 7b6e4b3111
commit 905819a1d5
9 changed files with 388 additions and 60 deletions

View 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;
}
}

View File

@@ -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' });
}