LCOV - code coverage report
Current view: top level - lib/models - contactlist.dart (source / functions) Hit Total Coverage
Test: lcov.info Lines: 0 77 0.0 %
Date: 2024-08-22 18:05:30 Functions: 0 0 -

          Line data    Source code
       1             : import 'package:cwtch/config.dart';
       2             : import 'package:flutter/widgets.dart';
       3             : 
       4             : import 'contact.dart';
       5             : import 'profileservers.dart';
       6             : 
       7             : class ContactListState extends ChangeNotifier {
       8             :   ProfileServerListState? servers;
       9             :   List<ContactInfoState> _contacts = [];
      10             :   String _filter = "";
      11           0 :   int get num => _contacts.length;
      12           0 :   int get numFiltered => isFiltered ? filteredList().length : num;
      13           0 :   bool get isFiltered => _filter != "";
      14           0 :   String get filter => _filter;
      15           0 :   set filter(String newVal) {
      16           0 :     _filter = newVal.toLowerCase();
      17           0 :     notifyListeners();
      18             :   }
      19             : 
      20           0 :   void connectServers(ProfileServerListState servers) {
      21           0 :     this.servers = servers;
      22             :   }
      23             : 
      24           0 :   List<ContactInfoState> filteredList() {
      25           0 :     if (!isFiltered) return _contacts;
      26           0 :     return _contacts.where((ContactInfoState c) => c.onion.toLowerCase().startsWith(_filter) || (c.nickname.toLowerCase().contains(_filter))).toList();
      27             :   }
      28             : 
      29           0 :   void addAll(Iterable<ContactInfoState> newContacts) {
      30           0 :     _contacts.addAll(newContacts);
      31           0 :     servers?.clearGroups();
      32           0 :     _contacts.forEach((contact) {
      33           0 :       if (contact.isGroup) {
      34           0 :         servers?.addGroup(contact);
      35             :       }
      36             :     });
      37           0 :     resort();
      38           0 :     notifyListeners();
      39             :   }
      40             : 
      41           0 :   void add(ContactInfoState newContact) {
      42           0 :     _contacts.add(newContact);
      43           0 :     if (newContact.isGroup) {
      44             :       // Copy the current known antispam value for the server
      45             :       // to the new contact. This lets us send messages straight away without
      46             :       // waiting for another update (or restarting the peer)...
      47             :       // Note for NEW servers we expect TokenServerInfo events to arrive after adding the group
      48             :       // this flow is only for existing servers...
      49             :       // FIXME: in Cwtch 1.14
      50             :       // NOTE: This is a bit hacky. Ideally this information would be stored per
      51             :       // Server not per Group, and creating a group would also trigger sharing
      52             :       // this information...on the backend all the accounting is done correctly.
      53           0 :       var otherGroups = servers?.getServer(newContact.server ?? "")?.groups;
      54           0 :       if (otherGroups != null && otherGroups.isNotEmpty) {
      55           0 :         EnvironmentConfig.debugLog("sharing antispam tickets to new group. FIXME: in Cwtch 1.14");
      56           0 :         var antispamTickets = otherGroups[0].antispamTickets;
      57           0 :         _contacts.last.antispamTickets = antispamTickets;
      58             :       }
      59           0 :       servers?.addGroup(newContact);
      60             :     }
      61           0 :     resort();
      62           0 :     notifyListeners();
      63             :   }
      64             : 
      65           0 :   void resort() {
      66           0 :     _contacts.sort((ContactInfoState a, ContactInfoState b) {
      67             :       // return -1 = a first in list
      68             :       // return 1 = b first in list
      69             : 
      70             :       // pinned contacts first
      71           0 :       if (a.pinned != true && b.pinned == true) return 1;
      72           0 :       if (a.pinned == true && b.pinned != true) return -1;
      73             :       // blocked contacts last
      74           0 :       if (a.isBlocked == true && b.isBlocked != true) return 1;
      75           0 :       if (a.isBlocked != true && b.isBlocked == true) return -1;
      76             :       // archive is next...
      77           0 :       if (!a.isArchived && b.isArchived) return -1;
      78           0 :       if (a.isArchived && !b.isArchived) return 1;
      79             : 
      80             :       // unapproved top
      81           0 :       if (a.isInvitation && !b.isInvitation) return -1;
      82           0 :       if (!a.isInvitation && b.isInvitation) return 1;
      83             : 
      84             :       // special sorting for contacts with no messages in either history
      85           0 :       if (a.lastMessageReceivedTime.millisecondsSinceEpoch == 0 && b.lastMessageReceivedTime.millisecondsSinceEpoch == 0) {
      86             :         // online contacts first
      87           0 :         if (a.isOnline() && !b.isOnline()) return -1;
      88           0 :         if (!a.isOnline() && b.isOnline()) return 1;
      89             :         // finally resort to onion
      90           0 :         return a.onion.toString().compareTo(b.onion.toString());
      91             :       }
      92             :       // finally... most recent history first
      93           0 :       if (a.lastMessageReceivedTime.millisecondsSinceEpoch == 0) return 1;
      94           0 :       if (b.lastMessageReceivedTime.millisecondsSinceEpoch == 0) return -1;
      95           0 :       return b.lastMessageReceivedTime.compareTo(a.lastMessageReceivedTime);
      96             :     });
      97             :     //<todo> if(changed) {
      98           0 :     notifyListeners();
      99             :     //} </todo>
     100             :   }
     101             : 
     102           0 :   void updateLastMessageReceivedTime(int forIdentifier, DateTime newMessageTime) {
     103           0 :     var contact = getContact(forIdentifier);
     104             :     if (contact == null) return;
     105             : 
     106             :     // Assert that the new time is after the current last message time AND that
     107             :     // new message time is before the current time.
     108           0 :     if (newMessageTime.isAfter(contact.lastMessageReceivedTime)) {
     109           0 :       if (newMessageTime.isBefore(DateTime.now().toLocal())) {
     110           0 :         contact.lastMessageReceivedTime = newMessageTime;
     111             :       } else {
     112             :         // Otherwise set the last message time to now...
     113           0 :         contact.lastMessageReceivedTime = DateTime.now().toLocal();
     114             :       }
     115           0 :       resort();
     116             :     }
     117             :   }
     118             : 
     119           0 :   List<ContactInfoState> get contacts => _contacts.sublist(0); //todo: copy?? dont want caller able to bypass changenotifier
     120             : 
     121           0 :   ContactInfoState? getContact(int identifier) {
     122           0 :     int idx = _contacts.indexWhere((element) => element.identifier == identifier);
     123           0 :     return idx >= 0 ? _contacts[idx] : null;
     124             :   }
     125             : 
     126           0 :   void removeContact(int identifier) {
     127           0 :     int idx = _contacts.indexWhere((element) => element.identifier == identifier);
     128           0 :     if (idx >= 0) {
     129           0 :       _contacts.removeAt(idx);
     130           0 :       notifyListeners();
     131             :     }
     132             :   }
     133             : 
     134           0 :   void removeContactByHandle(String handle) {
     135           0 :     int idx = _contacts.indexWhere((element) => element.onion == handle);
     136           0 :     _contacts.removeAt(idx);
     137           0 :     notifyListeners();
     138             :   }
     139             : 
     140           0 :   ContactInfoState? findContact(String byHandle) {
     141           0 :     int idx = _contacts.indexWhere((element) => element.onion == byHandle);
     142           0 :     return idx >= 0 ? _contacts[idx] : null;
     143             :   }
     144             : 
     145           0 :   void newMessage(int identifier, int messageID, DateTime timestamp, String senderHandle, String senderImage, bool isAuto, String data, String contenthash, bool selectedConversation) {
     146           0 :     getContact(identifier)?.newMessage(identifier, messageID, timestamp, senderHandle, senderImage, isAuto, data, contenthash, selectedConversation);
     147           0 :     updateLastMessageReceivedTime(identifier, DateTime.now());
     148             :   }
     149             : 
     150           0 :   int cacheMemUsage() {
     151           0 :     return _contacts.map((e) => e.messageCache.size()).fold(0, (previousValue, element) => previousValue + element);
     152             :   }
     153             : }

Generated by: LCOV version 1.14