add contact sorting and filtering
continuous-integration/drone/pr Build is passing
Details
continuous-integration/drone/pr Build is passing
Details
This commit is contained in:
parent
5605b8a336
commit
5714713dab
|
@ -55,6 +55,8 @@ class CwtchNotifier {
|
|||
contact.isInvitation = data["authorization"] == "unknown";
|
||||
contact.isBlocked = data["authorization"] == "blocked";
|
||||
}
|
||||
// contact.[status/isBlocked] might change the list's sort order
|
||||
profileCN.getProfile(data["ProfileOnion"]).contactList.resort();
|
||||
}
|
||||
break;
|
||||
case "NewMessageFromPeer":
|
||||
|
@ -165,6 +167,7 @@ class CwtchNotifier {
|
|||
contact.status = data["ConnectionState"];
|
||||
}
|
||||
});
|
||||
profileCN.getProfile(data["ProfileOnion"]).contactList.resort();
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
|
@ -80,7 +80,20 @@ class ProfileListState extends ChangeNotifier {
|
|||
|
||||
class ContactListState extends ChangeNotifier {
|
||||
List<ContactInfoState> _contacts = [];
|
||||
String _filter;
|
||||
int get num => _contacts.length;
|
||||
int get numFiltered => isFiltered ? filteredList().length : num;
|
||||
bool get isFiltered => _filter != null && _filter != "";
|
||||
String get filter => _filter;
|
||||
set filter(String newVal) {
|
||||
_filter = newVal;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
List<ContactInfoState> filteredList() {
|
||||
if (!isFiltered) return contacts;
|
||||
return _contacts.where((ContactInfoState c) => c.onion.contains(_filter) || (c.nickname != null && c.nickname.contains(_filter))).toList();
|
||||
}
|
||||
|
||||
void addAll(Iterable<ContactInfoState> newContacts) {
|
||||
_contacts.addAll(newContacts);
|
||||
|
@ -92,15 +105,24 @@ class ContactListState extends ChangeNotifier {
|
|||
notifyListeners();
|
||||
}
|
||||
|
||||
void updateLastMessageTime(String forOnion, DateTime newVal) {
|
||||
var contact = getContact(forOnion);
|
||||
if (contact == null) return;
|
||||
|
||||
contact.lastMessageTime = newVal;
|
||||
void resort() {
|
||||
_contacts.sort((ContactInfoState a, ContactInfoState b) {
|
||||
if (a.lastMessageTime == null && b.lastMessageTime == null) return b.onion.compareTo(a.onion);
|
||||
if (a.lastMessageTime == null) return 1;
|
||||
if (b.lastMessageTime == null) return -1;
|
||||
// return -1 = a first in list
|
||||
// return 1 = b first in list
|
||||
// blocked contacts last
|
||||
if (a.isBlocked == true && b.isBlocked != true) return 1;
|
||||
if (a.isBlocked != true && b.isBlocked == true) return -1;
|
||||
// special sorting for contacts with no messages in either history
|
||||
if (a.lastMessageTime.millisecondsSinceEpoch == 0 && b.lastMessageTime.millisecondsSinceEpoch == 0) {
|
||||
// online contacts first
|
||||
if (a.isOnline() && !b.isOnline()) return -1;
|
||||
if (!a.isOnline() && b.isOnline()) return 1;
|
||||
// finally resort to onion
|
||||
return a.onion.toString().compareTo(b.onion.toString());
|
||||
}
|
||||
// finally... most recent history first
|
||||
if (a.lastMessageTime.millisecondsSinceEpoch == 0) return 1;
|
||||
if (b.lastMessageTime.millisecondsSinceEpoch == 0) return -1;
|
||||
return b.lastMessageTime.compareTo(a.lastMessageTime);
|
||||
});
|
||||
//<todo> if(changed) {
|
||||
|
@ -108,6 +130,14 @@ class ContactListState extends ChangeNotifier {
|
|||
//} </todo>
|
||||
}
|
||||
|
||||
void updateLastMessageTime(String forOnion, DateTime newVal) {
|
||||
var contact = getContact(forOnion);
|
||||
if (contact == null) return;
|
||||
|
||||
contact.lastMessageTime = newVal;
|
||||
resort();
|
||||
}
|
||||
|
||||
List<ContactInfoState> get contacts => _contacts.sublist(0); //todo: copy?? dont want caller able to bypass changenotifier
|
||||
|
||||
ContactInfoState getContact(String onion) {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_app/widgets/contactrow.dart';
|
||||
import 'package:flutter_app/widgets/profileimage.dart';
|
||||
import 'package:flutter_app/widgets/textfield.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import '../settings.dart';
|
||||
import 'addcontactview.dart';
|
||||
|
@ -15,6 +16,15 @@ class ContactsView extends StatefulWidget {
|
|||
}
|
||||
|
||||
class _ContactsViewState extends State<ContactsView> {
|
||||
TextEditingController ctrlrFilter;
|
||||
bool showSearchBar = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
ctrlrFilter = new TextEditingController(text: Provider.of<ContactListState>(context, listen: false).filter);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
|
@ -42,6 +52,14 @@ class _ContactsViewState extends State<ContactsView> {
|
|||
IconButton(
|
||||
icon: Icon(Icons.bug_report),
|
||||
onPressed: _debugFakeMessage,
|
||||
),
|
||||
IconButton(
|
||||
// need both conditions for displaying initial empty textfield and also allowing filters to be cleared if this widget gets lost/reset
|
||||
icon: Icon(showSearchBar || Provider.of<ContactListState>(context).isFiltered ? Icons.search_off : Icons.search),
|
||||
onPressed: () {
|
||||
Provider.of<ContactListState>(context).filter = "";
|
||||
setState((){showSearchBar = !showSearchBar;});
|
||||
}
|
||||
)
|
||||
],
|
||||
),
|
||||
|
@ -50,13 +68,21 @@ class _ContactsViewState extends State<ContactsView> {
|
|||
tooltip: AppLocalizations.of(context).tooltipAddContact,
|
||||
child: const Icon(Icons.person_add_sharp),
|
||||
),
|
||||
body: _buildContactList(),
|
||||
body: showSearchBar || Provider.of<ContactListState>(context).isFiltered ? _buildFilterable() : _buildContactList(),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildFilterable() {
|
||||
//todo: translate
|
||||
Widget txtfield = CwtchTextField(controller: ctrlrFilter, labelText: AppLocalizations.of(context).search, onChanged: (newVal){
|
||||
Provider.of<ContactListState>(context, listen: false).filter = newVal;
|
||||
});
|
||||
return Column(children: [Padding(padding: EdgeInsets.all(2), child: txtfield), Expanded(child: _buildContactList())]);
|
||||
}
|
||||
|
||||
Widget _buildContactList() {
|
||||
final tiles = Provider.of<ContactListState>(context).contacts.map((ContactInfoState contact) {
|
||||
return ChangeNotifierProvider<ContactInfoState>.value(value: contact, child: ContactRow());
|
||||
final tiles = Provider.of<ContactListState>(context).filteredList().map((ContactInfoState contact) {
|
||||
return ChangeNotifierProvider<ContactInfoState>.value(key: ValueKey(contact.onion), value: contact, builder: (_,__) => ContactRow());
|
||||
});
|
||||
final divided = ListTile.divideTiles(
|
||||
context: context,
|
||||
|
|
Loading…
Reference in New Issue