diff --git a/lib/cwtch/cwtchNotifier.dart b/lib/cwtch/cwtchNotifier.dart index ebd3979c..acd1a35b 100644 --- a/lib/cwtch/cwtchNotifier.dart +++ b/lib/cwtch/cwtchNotifier.dart @@ -149,7 +149,18 @@ class CwtchNotifier { profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["GroupID"])!.unreadMessages++; } profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["GroupID"])!.totalMessages++; - profileCN.getProfile(data["ProfileOnion"])?.contactList.updateLastMessageTime(data["GroupID"], DateTime.now()); + var timestampSent = DateTime.tryParse(data['TimestampSent'])!; + // TODO: There are 2 timestamps associated with a new group message - time sent and time received. + // Sent refers to the time a profile alleges they sent a message + // Received refers to the time we actually saw the message from the server + // These can obviously be very different for legitimate reasons. + // We also maintain a relative hash-link through PreviousMessageSignature which is the ground truth for + // order. + // In the future we will want to combine these 3 ordering mechanisms into a cohesive view of the timeline + // For now we perform some minimal checks on the sent timestamp to use to provide a useful ordering for honest contacts + // and ensure that malicious contacts in groups can only set this timestamp to a value within the range of `last seen message time` + // and `local now`. + profileCN.getProfile(data["ProfileOnion"])?.contactList.updateLastMessageTime(data["GroupID"], timestampSent.toLocal()); notificationManager.notify("New Message From Group!"); } else { // from me (already displayed - do not update counter) diff --git a/lib/model.dart b/lib/model.dart index 46a8b508..1baa71b0 100644 --- a/lib/model.dart +++ b/lib/model.dart @@ -153,12 +153,21 @@ class ContactListState extends ChangeNotifier { //} } - void updateLastMessageTime(String forOnion, DateTime newVal) { + void updateLastMessageTime(String forOnion, DateTime newMessageTime) { var contact = getContact(forOnion); if (contact == null) return; - contact.lastMessageTime = newVal; - resort(); + // 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.lastMessageTime)) { + if (newMessageTime.isBefore(DateTime.now().toLocal())) { + contact.lastMessageTime = newMessageTime; + } else { + // Otherwise set the last message time to now... + contact.lastMessageTime = DateTime.now().toLocal(); + } + resort(); + } } List get contacts => _contacts.sublist(0); //todo: copy?? dont want caller able to bypass changenotifier