monorepo/dialer/lib/features/contacts/contact_state.dart
AlexisDanlos 110020bb4b
All checks were successful
/ mirror (push) Successful in 8s
/ build-stealth (push) Successful in 8m23s
/ build (push) Successful in 8m26s
WIP: toogle favorite fix
set favorites -> works
unset favorites -> unstable
2025-03-07 17:52:10 +01:00

194 lines
5.7 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_contacts/flutter_contacts.dart';
import 'package:permission_handler/permission_handler.dart';
import '../../services/contact_service.dart';
class ContactState extends StatefulWidget {
final Widget child;
const ContactState({super.key, required this.child});
static _ContactStateState of(BuildContext context) {
return context
.dependOnInheritedWidgetOfExactType<_InheritedContactState>()!
.data;
}
@override
_ContactStateState createState() => _ContactStateState();
}
class _ContactStateState extends State<ContactState> {
final ContactService _contactService = ContactService();
List<Contact> _allContacts = [];
List<Contact> _favoriteContacts = [];
bool _loading = true;
double _scrollOffset = 0.0;
Contact? _selfContact = Contact();
bool _permissionRequestInProgress = false;
// Getters for all contacts and favorites
List<Contact> get contacts => _allContacts;
List<Contact> get favoriteContacts => _favoriteContacts;
bool get loading => _loading;
double get scrollOffset => _scrollOffset;
Contact? get selfContact => _selfContact;
@override
void initState() {
super.initState();
_initializeContacts();
}
Future<void> _initializeContacts() async {
try {
final status = await Permission.contacts.status;
if (status.isGranted) {
await fetchContacts();
} else {
final result = await Permission.contacts.request();
if (result.isGranted) {
await fetchContacts();
} else {
setState(() => _loading = false);
}
}
} catch (e) {
debugPrint('Error initializing contacts: $e');
setState(() => _loading = false);
}
}
void _onContactChange() => fetchContacts();
@override
void dispose() {
FlutterContacts.removeListener(_onContactChange);
super.dispose();
}
// Fetch all contacts
Future<void> fetchContacts() async {
setState(() => _loading = true);
try {
List<Contact> contacts = await _contactService.fetchContacts();
_processContacts(contacts);
} finally {
setState(() => _loading = false);
}
}
// Fetch only favorite contacts
Future<void> fetchFavoriteContacts() async {
setState(() => _loading = true);
try {
List<Contact> contacts = await _contactService.fetchFavoriteContacts();
setState(() => _favoriteContacts = contacts);
} finally {
setState(() => _loading = false);
}
}
void _processContacts(List<Contact> contacts) {
_selfContact = contacts.firstWhere(
(contact) => contact.displayName.toLowerCase() == "user",
orElse: () => Contact(),
);
if (_selfContact!.phones.isEmpty) {
debugPrint("Self contact has no phone numbers");
_selfContact = null;
}
contacts = contacts.where((contact) => contact.phones.isNotEmpty).toList();
contacts.sort((a, b) => a.displayName.compareTo(b.displayName));
setState(() {
_allContacts = contacts;
_favoriteContacts =
contacts.where((contact) => contact.isStarred).toList();
_selfContact = _selfContact;
});
}
Future<void> addNewContact(Contact contact) async {
await _contactService.addNewContact(contact);
await fetchContacts();
}
// Add this new method to update a single contact in state without reloading all contacts
Future<void> updateContactInState(Contact updatedContact) async {
setState(() {
// Find and update in the all contacts list
final allIndex = _allContacts.indexWhere((c) => c.id == updatedContact.id);
if (allIndex != -1) {
_allContacts[allIndex] = updatedContact;
}
// Update the favorites list based on the star status
if (updatedContact.isStarred) {
// Add to favorites if not already there
if (!_favoriteContacts.any((c) => c.id == updatedContact.id)) {
_favoriteContacts.add(updatedContact);
} else {
// If already in favorites, update it
final favIndex = _favoriteContacts.indexWhere((c) => c.id == updatedContact.id);
if (favIndex != -1) {
_favoriteContacts[favIndex] = updatedContact;
}
}
} else {
// Remove from favorites if it's there
_favoriteContacts.removeWhere((c) => c.id == updatedContact.id);
}
// Re-sort both lists to maintain alphabetical order
_allContacts.sort((a, b) => a.displayName.compareTo(b.displayName));
_favoriteContacts.sort((a, b) => a.displayName.compareTo(b.displayName));
});
}
void setScrollOffset(double offset) {
setState(() {
_scrollOffset = offset;
});
}
bool doesContactExist(Contact contact) {
// Example: consider it "existing" if there's a matching phone number
for (final existing in _allContacts) {
if (existing.toVCard() == contact.toVCard()) {
return true;
}
// for (final phone in existing.phones) {
// for (final newPhone in contact.phones) {
// // Simple exact match; you can do more advanced logic
// if (phone.normalizedNumber == newPhone.normalizedNumber) {
// return true;
// }
// }
// } We might switch to finer and smarter logic later, ex: remove trailing spaces, capitals
}
return false;
}
@override
Widget build(BuildContext context) {
return _InheritedContactState(
data: this,
child: widget.child,
);
}
}
class _InheritedContactState extends InheritedWidget {
final _ContactStateState data;
const _InheritedContactState({required this.data, required super.child});
@override
bool updateShouldNotify(_InheritedContactState oldWidget) => true;
}