cwtch-ui/lib/models/contactlist.dart

154 lines
5.8 KiB
Dart

import 'package:cwtch/config.dart';
import 'package:flutter/widgets.dart';
import 'contact.dart';
import 'profileservers.dart';
class ContactListState extends ChangeNotifier {
ProfileServerListState? servers;
List<ContactInfoState> _contacts = [];
String _filter = "";
int get num => _contacts.length;
int get numFiltered => isFiltered ? filteredList().length : num;
bool get isFiltered => _filter != "";
String get filter => _filter;
set filter(String newVal) {
_filter = newVal.toLowerCase();
notifyListeners();
}
void connectServers(ProfileServerListState servers) {
this.servers = servers;
}
List<ContactInfoState> filteredList() {
if (!isFiltered) return _contacts;
return _contacts.where((ContactInfoState c) => c.onion.toLowerCase().startsWith(_filter) || (c.nickname.toLowerCase().contains(_filter))).toList();
}
void addAll(Iterable<ContactInfoState> newContacts) {
_contacts.addAll(newContacts);
servers?.clearGroups();
_contacts.forEach((contact) {
if (contact.isGroup) {
servers?.addGroup(contact);
}
});
resort();
notifyListeners();
}
void add(ContactInfoState newContact) {
_contacts.add(newContact);
if (newContact.isGroup) {
// Copy the current known antispam value for the server
// to the new contact. This lets us send messages straight away without
// waiting for another update (or restarting the peer)...
// Note for NEW servers we expect TokenServerInfo events to arrive after adding the group
// this flow is only for existing servers...
// FIXME: in Cwtch 1.14
// NOTE: This is a bit hacky. Ideally this information would be stored per
// Server not per Group, and creating a group would also trigger sharing
// this information...on the backend all the accounting is done correctly.
var otherGroups = servers?.getServer(newContact.server ?? "")?.groups;
if (otherGroups != null && otherGroups.isNotEmpty) {
EnvironmentConfig.debugLog("sharing antispam tickets to new group. FIXME: in Cwtch 1.14");
var antispamTickets = otherGroups[0].antispamTickets;
_contacts.last.antispamTickets = antispamTickets;
}
servers?.addGroup(newContact);
}
resort();
notifyListeners();
}
void resort() {
_contacts.sort((ContactInfoState a, ContactInfoState b) {
// return -1 = a first in list
// return 1 = b first in list
// pinned contacts first
if (a.pinned != true && b.pinned == true) return 1;
if (a.pinned == true && b.pinned != true) return -1;
// blocked contacts last
if (a.isBlocked == true && b.isBlocked != true) return 1;
if (a.isBlocked != true && b.isBlocked == true) return -1;
// archive is next...
if (!a.isArchived && b.isArchived) return -1;
if (a.isArchived && !b.isArchived) return 1;
// unapproved top
if (a.isInvitation && !b.isInvitation) return -1;
if (!a.isInvitation && b.isInvitation) return 1;
// special sorting for contacts with no messages in either history
if (a.lastMessageReceivedTime.millisecondsSinceEpoch == 0 && b.lastMessageReceivedTime.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.lastMessageReceivedTime.millisecondsSinceEpoch == 0) return 1;
if (b.lastMessageReceivedTime.millisecondsSinceEpoch == 0) return -1;
return b.lastMessageReceivedTime.compareTo(a.lastMessageReceivedTime);
});
//<todo> if(changed) {
notifyListeners();
//} </todo>
}
void updateLastMessageReceivedTime(int forIdentifier, DateTime newMessageTime) {
var contact = getContact(forIdentifier);
if (contact == null) return;
// Assert that the new time is after the current last message time AND that
// new message time is before the current time.
if (newMessageTime.isAfter(contact.lastMessageReceivedTime)) {
if (newMessageTime.isBefore(DateTime.now().toLocal())) {
contact.lastMessageReceivedTime = newMessageTime;
} else {
// Otherwise set the last message time to now...
contact.lastMessageReceivedTime = DateTime.now().toLocal();
}
resort();
}
}
List<ContactInfoState> get contacts => _contacts.sublist(0); //todo: copy?? dont want caller able to bypass changenotifier
ContactInfoState? getContact(int identifier) {
int idx = _contacts.indexWhere((element) => element.identifier == identifier);
return idx >= 0 ? _contacts[idx] : null;
}
void removeContact(int identifier) {
int idx = _contacts.indexWhere((element) => element.identifier == identifier);
if (idx >= 0) {
_contacts.removeAt(idx);
notifyListeners();
}
}
void removeContactByHandle(String handle) {
int idx = _contacts.indexWhere((element) => element.onion == handle);
_contacts.removeAt(idx);
notifyListeners();
}
ContactInfoState? findContact(String byHandle) {
int idx = _contacts.indexWhere((element) => element.onion == byHandle);
return idx >= 0 ? _contacts[idx] : null;
}
void newMessage(int identifier, int messageID, DateTime timestamp, String senderHandle, String senderImage, bool isAuto, String data, String contenthash, bool selectedConversation) {
getContact(identifier)?.newMessage(identifier, messageID, timestamp, senderHandle, senderImage, isAuto, data, contenthash, selectedConversation);
updateLastMessageReceivedTime(identifier, DateTime.now());
}
int cacheMemUsage() {
return _contacts.map((e) => e.messageCache.size()).fold(0, (previousValue, element) => previousValue + element);
}
}