Feat: contact-modal et refonte du contact-state pour la page de favori et possibilité de update les contacts. Aussi mis la composition page avec le service de contact, on évite de fetch dans la page directement Reviewed-on: icing/G-EIP-700-TLS-7-1-eip-stephane.corbiere#10 Co-authored-by: Florian Griffon <florian.griffon@epitech.eu> Co-committed-by: Florian Griffon <florian.griffon@epitech.eu>
223 lines
8.2 KiB
Dart
223 lines
8.2 KiB
Dart
import 'package:dialer/widgets/username_color_generator.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_contacts/flutter_contacts.dart';
|
|
import '../contact_state.dart';
|
|
import '../../../widgets/color_darkener.dart';
|
|
import 'add_contact_button.dart';
|
|
import 'contact_modal.dart';
|
|
import 'share_own_qr.dart';
|
|
|
|
class AlphabetScrollPage extends StatefulWidget {
|
|
final double scrollOffset;
|
|
final List<Contact> contacts;
|
|
|
|
const AlphabetScrollPage({
|
|
super.key,
|
|
required this.scrollOffset,
|
|
required this.contacts,
|
|
});
|
|
|
|
@override
|
|
_AlphabetScrollPageState createState() => _AlphabetScrollPageState();
|
|
}
|
|
|
|
class _AlphabetScrollPageState extends State<AlphabetScrollPage> {
|
|
late ScrollController _scrollController;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_scrollController =
|
|
ScrollController(initialScrollOffset: widget.scrollOffset);
|
|
_scrollController.addListener(_onScroll);
|
|
}
|
|
|
|
void _onScroll() {
|
|
final contactState = ContactState.of(context);
|
|
contactState.setScrollOffset(_scrollController.offset);
|
|
}
|
|
|
|
Future<void> _refreshContacts() async {
|
|
final contactState = ContactState.of(context);
|
|
try {
|
|
await contactState.fetchContacts();
|
|
} catch (e) {
|
|
print('Error refreshing contacts: $e');
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(content: Text('Failed to refresh contacts')),
|
|
);
|
|
}
|
|
}
|
|
|
|
void _toggleFavorite(Contact contact) async {
|
|
try {
|
|
if (await FlutterContacts.requestPermission()) {
|
|
Contact? fullContact = await FlutterContacts.getContact(contact.id,
|
|
withProperties: true,
|
|
withAccounts: true,
|
|
withPhoto: true,
|
|
withThumbnail: true);
|
|
|
|
if (fullContact != null) {
|
|
fullContact.isStarred = !fullContact.isStarred;
|
|
await FlutterContacts.updateContact(fullContact);
|
|
}
|
|
await _refreshContacts();
|
|
} else {
|
|
print("Could not fetch contact details");
|
|
}
|
|
} catch (e) {
|
|
print("Error updating favorite status: $e");
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(content: Text('Failed to update contact favorite status')),
|
|
);
|
|
}
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final contacts = widget.contacts;
|
|
final selfContact = ContactState.of(context).selfContact;
|
|
|
|
Map<String, List<Contact>> alphabetizedContacts = {};
|
|
for (var contact in contacts) {
|
|
String firstLetter = contact.displayName.isNotEmpty
|
|
? contact.displayName[0].toUpperCase()
|
|
: '#';
|
|
if (!alphabetizedContacts.containsKey(firstLetter)) {
|
|
alphabetizedContacts[firstLetter] = [];
|
|
}
|
|
alphabetizedContacts[firstLetter]!.add(contact);
|
|
}
|
|
|
|
List<String> alphabetKeys = alphabetizedContacts.keys.toList()..sort();
|
|
|
|
return Scaffold(
|
|
backgroundColor: Colors.black,
|
|
body: Column(
|
|
children: [
|
|
// Top buttons row
|
|
Container(
|
|
color: Colors.black,
|
|
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
|
|
child: Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
AddContactButton(),
|
|
QRCodeButton(contacts: contacts, selfContact: selfContact),
|
|
],
|
|
),
|
|
),
|
|
// Contact List
|
|
Expanded(
|
|
child: ListView.builder(
|
|
controller: _scrollController,
|
|
itemCount: alphabetKeys.length,
|
|
itemBuilder: (context, index) {
|
|
String letter = alphabetKeys[index];
|
|
List<Contact> contactsForLetter = alphabetizedContacts[letter]!;
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
// Alphabet Letter Header
|
|
Padding(
|
|
padding: const EdgeInsets.symmetric(
|
|
vertical: 8.0, horizontal: 16.0),
|
|
child: Text(
|
|
letter,
|
|
style: TextStyle(
|
|
fontSize: 28,
|
|
fontWeight: FontWeight.bold,
|
|
color: Colors.white,
|
|
),
|
|
),
|
|
),
|
|
// Contact Entries
|
|
...contactsForLetter.map((contact) {
|
|
String phoneNumber = contact.phones.isNotEmpty
|
|
? contact.phones.first.number
|
|
: 'No phone number';
|
|
Color avatarColor =
|
|
generateColorFromName(contact.displayName);
|
|
return ListTile(
|
|
leading: (contact.thumbnail != null &&
|
|
contact.thumbnail!.isNotEmpty)
|
|
? CircleAvatar(
|
|
backgroundImage:
|
|
MemoryImage(contact.thumbnail!),
|
|
)
|
|
: CircleAvatar(
|
|
backgroundColor: avatarColor,
|
|
child: Text(
|
|
contact.displayName.isNotEmpty
|
|
? contact.displayName[0].toUpperCase()
|
|
: '?',
|
|
style: TextStyle(
|
|
color: darken(avatarColor, 0.4)),
|
|
),
|
|
),
|
|
title: Text(contact.displayName,
|
|
style: TextStyle(color: Colors.white)),
|
|
subtitle: Text(phoneNumber,
|
|
style: TextStyle(color: Colors.white70)),
|
|
onTap: () {
|
|
showModalBottomSheet(
|
|
context: context,
|
|
isScrollControlled: true,
|
|
backgroundColor: Colors.transparent,
|
|
builder: (context) {
|
|
return ContactModal(
|
|
contact: contact,
|
|
onEdit: () async {
|
|
if (await FlutterContacts.requestPermission()) {
|
|
final updatedContact =
|
|
await FlutterContacts.openExternalEdit(
|
|
contact.id);
|
|
if (updatedContact != null) {
|
|
await _refreshContacts();
|
|
Navigator.of(context).pop();
|
|
ScaffoldMessenger.of(context)
|
|
.showSnackBar(
|
|
SnackBar(
|
|
content: Text(
|
|
'${contact.displayName} updated successfully!'),
|
|
),
|
|
);
|
|
} else {
|
|
ScaffoldMessenger.of(context)
|
|
.showSnackBar(
|
|
SnackBar(
|
|
content:
|
|
Text('Edit canceled or failed.'),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
},
|
|
onToggleFavorite: () {
|
|
_toggleFavorite(contact);
|
|
},
|
|
isFavorite: contact.isStarred,
|
|
);
|
|
},
|
|
);
|
|
},
|
|
);
|
|
}),
|
|
],
|
|
);
|
|
},
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_scrollController.dispose();
|
|
super.dispose();
|
|
}
|
|
}
|