feat(contacts): имена из телефонной книги везде + тумблер в дев-меню

This commit is contained in:
klockky
2026-07-16 11:49:52 +03:00
parent cf8e50c6f7
commit 131c1dc775
13 changed files with 333 additions and 15 deletions
+24 -5
View File
@@ -60,8 +60,14 @@ class PhoneLookupResult {
final int id;
final String? name;
final String? avatarUrl;
final int phone;
const PhoneLookupResult({required this.id, this.name, this.avatarUrl});
const PhoneLookupResult({
required this.id,
this.name,
this.avatarUrl,
this.phone = 0,
});
}
class ContactPhotos {
@@ -88,6 +94,14 @@ class ContactsModule {
final id = contact['id'];
if (id is! int) return null;
primeContactCache(contact);
final payloadPhone = contact['phone'];
final resolvedPhone = payloadPhone is int && payloadPhone > 0
? payloadPhone
: int.tryParse(normalized.substring(1)) ?? 0;
ContactCache.putPhone(id, resolvedPhone);
String? name;
final names = contact['names'];
if (names is List) {
@@ -105,6 +119,7 @@ class ContactsModule {
id: id,
name: name,
avatarUrl: contact['baseUrl'] as String?,
phone: resolvedPhone,
);
}
@@ -154,7 +169,7 @@ class ContactsModule {
row['phone'] = phone;
}
await AppDatabase.saveContacts([row]);
if (contact != null) _primeContactCache(contact);
if (contact != null) primeContactCache(contact);
revision.value++;
return CachedContact.fromDbRow(row);
}
@@ -171,7 +186,7 @@ class ContactsModule {
final contact = raw.cast<dynamic, dynamic>();
final row = _parseContact(contact, accountId);
if (row != null) rows.add(row);
_primeContactCache(contact);
primeContactCache(contact);
}
if (rows.isNotEmpty) {
@@ -200,16 +215,19 @@ class ContactsModule {
for (final raw in contacts.whereType<Map>()) {
if (raw['id'] != accountId) continue;
final contact = raw.cast<dynamic, dynamic>();
_primeContactCache(contact);
primeContactCache(contact);
return ProfileData.fromServerMap(contact);
}
return null;
}
static void _primeContactCache(Map<dynamic, dynamic> contact) {
static void primeContactCache(Map<dynamic, dynamic> contact) {
final id = contact['id'];
if (id is! int) return;
final phone = contact['phone'];
if (phone is int) ContactCache.putPhone(id, phone);
final names = contact['names'];
if (names is List && names.isNotEmpty) {
final nameRaw = names.firstWhere(
@@ -297,6 +315,7 @@ class ContactsModule {
static Future<void> primeCacheFromDb(int accountId) async {
final contacts = await getContacts(accountId);
for (final c in contacts) {
ContactCache.putPhone(c.id, c.phone);
final fullName = (c.lastName != null && c.lastName!.isNotEmpty)
? '${c.firstName} ${c.lastName}'
: c.firstName;
+15 -1
View File
@@ -4,6 +4,7 @@ import 'package:flutter/foundation.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../api.dart';
import '../../core/config/komet_settings.dart';
import '../../core/contacts/device_contacts_service.dart';
import '../../core/protocol/opcode_map.dart';
import '../../core/protocol/packet.dart';
import '../../core/storage/app_database.dart';
@@ -17,6 +18,7 @@ class ContactCache {
static final Map<int, String> _nameCache = {};
static final Map<int, String> _avatarCache = {};
static final Map<int, Set<String>> _optionsCache = {};
static final Map<int, int> _phoneCache = {};
static const _prefsKey = 'contact_cache_v1';
static Timer? _saveTimer;
@@ -61,7 +63,18 @@ class ContactCache {
_scheduleSave();
}
static String? get(int id) => _nameCache[id];
static void putPhone(int id, int phone) {
if (phone > 0) _phoneCache[id] = phone;
}
static String? get(int id) {
final phone = _phoneCache[id];
if (phone != null) {
final book = DeviceContactsService.nameForPhone(phone);
if (book != null && book.isNotEmpty) return book;
}
return _nameCache[id];
}
static String? getAvatar(int id) => _avatarCache[id];
static Set<String>? getOptions(int id) => _optionsCache[id];
static bool isOfficial(int id) =>
@@ -71,6 +84,7 @@ class ContactCache {
_nameCache.clear();
_avatarCache.clear();
_optionsCache.clear();
_phoneCache.clear();
_saveTimer?.cancel();
_saveTimer = null;
unawaited(_wipePersisted());