154 lines
5.8 KiB
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);
|
|
}
|
|
}
|