import 'dart:async'; import 'package:contacts_plus_plus/api_client.dart'; import 'package:contacts_plus_plus/apis/friend_api.dart'; import 'package:contacts_plus_plus/apis/message_api.dart'; import 'package:contacts_plus_plus/models/friend.dart'; import 'package:contacts_plus_plus/models/message.dart'; import 'package:contacts_plus_plus/widgets/expanding_input_fab.dart'; import 'package:contacts_plus_plus/widgets/friend_list_tile.dart'; import 'package:contacts_plus_plus/widgets/settings_page.dart'; import 'package:flutter/material.dart'; class FriendsList extends StatefulWidget { const FriendsList({super.key}); @override State createState() => _FriendsListState(); } class _FriendsListState extends State { Future>? _friendsFuture; ClientHolder? _clientHolder; Timer? _debouncer; String _searchFilter = ""; final _unreads = >{}; @override void dispose() { _debouncer?.cancel(); super.dispose(); } @override void didChangeDependencies() { super.didChangeDependencies(); final clientHolder = ClientHolder.of(context); if (_clientHolder != clientHolder) { _clientHolder = clientHolder; _refreshFriendsList(); } } void _refreshFriendsList() { _friendsFuture = FriendApi.getFriendsList(_clientHolder!.client).then((Iterable value) async { final unreadMessages = await MessageApi.getUserMessages(_clientHolder!.client, unreadOnly: true); _unreads.clear(); for (final msg in unreadMessages) { if (msg.senderId != _clientHolder!.client.userId) { final value = _unreads[msg.senderId]; if (value == null) { _unreads[msg.senderId] = [msg]; } else { value.add(msg); } } } final friends = value.toList() ..sort((a, b) { var aVal = _unreads.containsKey(a.id) ? -3 : 0; var bVal = _unreads.containsKey(b.id) ? -3 : 0; aVal -= a.userStatus.lastStatusChange.compareTo(b.userStatus.lastStatusChange); aVal += a.userStatus.onlineStatus.compareTo(b.userStatus.onlineStatus) * 2; return aVal.compareTo(bVal); }); return friends; }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text("Contacts++"), actions: [ IconButton( onPressed: () { Navigator.of(context).push(MaterialPageRoute(builder: (context) => const SettingsPage())); }, icon: const Icon(Icons.settings), ) ], ), body: Stack( children: [ RefreshIndicator( onRefresh: () async { _refreshFriendsList(); await _friendsFuture; }, child: FutureBuilder( future: _friendsFuture, builder: (context, snapshot) { if (snapshot.hasData) { var friends = (snapshot.data as List); if (_searchFilter.isNotEmpty) { friends = friends.where((element) => element.username.contains(_searchFilter)).toList(); friends.sort((a, b) => a.username.length.compareTo(b.username.length)); } return ListView.builder( itemCount: friends.length, itemBuilder: (context, index) { final friend = friends[index]; final unread = _unreads[friend.id] ?? []; return FriendListTile( friend: friend, unreads: unread.length, onTap: () async { if (unread.isNotEmpty) { final readBatch = MarkReadBatch( senderId: _clientHolder!.client.userId, ids: unread.map((e) => e.id).toList(), readTime: DateTime.now(), ); _clientHolder!.hub.markMessagesRead(readBatch); } setState(() { unread.clear(); }); }, ); }, ); } else if (snapshot.hasError) { FlutterError.reportError(FlutterErrorDetails(exception: snapshot.error!, stack: snapshot.stackTrace)); return Center( child: Padding( padding: const EdgeInsets.all(64), child: Text( "Something went wrong: ${snapshot.error}", softWrap: true, style: Theme .of(context) .textTheme .labelMedium, ), ), ); } else { return const SizedBox.shrink(); } } ), ), Align( alignment: Alignment.bottomCenter, child: ExpandingInputFab( onInputChanged: (String text) { setState(() { _searchFilter = text; }); }, onExpansionChanged: (expanded) { if (_debouncer?.isActive ?? false) _debouncer?.cancel(); if (!expanded) { setState(() { _searchFilter = ""; }); } }, ), ), ], ), ); } }