1#![allow(dead_code)] use std::{
121 collections::{BTreeMap, BTreeSet},
122 num::NonZeroUsize,
123};
124
125use eyeball_im::Vector;
126use matrix_sdk_common::{deserialized_responses::TimelineEvent, ring_buffer::RingBuffer};
127use ruma::{
128 events::{
129 poll::{start::PollStartEventContent, unstable_start::UnstablePollStartEventContent},
130 receipt::{ReceiptEventContent, ReceiptThread, ReceiptType},
131 room::message::Relation,
132 AnySyncMessageLikeEvent, AnySyncTimelineEvent, OriginalSyncMessageLikeEvent,
133 SyncMessageLikeEvent,
134 },
135 serde::Raw,
136 EventId, OwnedEventId, OwnedUserId, RoomId, UserId,
137};
138use serde::{Deserialize, Serialize};
139use tracing::{debug, instrument, trace, warn};
140
141#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
142struct LatestReadReceipt {
143 event_id: OwnedEventId,
146}
147
148#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
153pub struct RoomReadReceipts {
154 pub num_unread: u64,
156
157 pub num_notifications: u64,
159
160 pub num_mentions: u64,
163
164 #[serde(default)]
167 latest_active: Option<LatestReadReceipt>,
168
169 #[serde(default = "new_nonempty_ring_buffer")]
178 pending: RingBuffer<OwnedEventId>,
179}
180
181impl Default for RoomReadReceipts {
182 fn default() -> Self {
183 Self {
184 num_unread: Default::default(),
185 num_notifications: Default::default(),
186 num_mentions: Default::default(),
187 latest_active: Default::default(),
188 pending: new_nonempty_ring_buffer(),
189 }
190 }
191}
192
193fn new_nonempty_ring_buffer() -> RingBuffer<OwnedEventId> {
194 RingBuffer::new(NonZeroUsize::new(10).unwrap())
197}
198
199impl RoomReadReceipts {
200 #[inline(always)]
205 fn process_event(&mut self, event: &TimelineEvent, user_id: &UserId) {
206 if marks_as_unread(event.raw(), user_id) {
207 self.num_unread += 1;
208 }
209
210 let mut has_notify = false;
211 let mut has_mention = false;
212
213 let Some(actions) = event.push_actions.as_ref() else {
214 return;
215 };
216
217 for action in actions.iter() {
218 if !has_notify && action.should_notify() {
219 self.num_notifications += 1;
220 has_notify = true;
221 }
222 if !has_mention && action.is_highlight() {
223 self.num_mentions += 1;
224 has_mention = true;
225 }
226 }
227 }
228
229 #[inline(always)]
230 fn reset(&mut self) {
231 self.num_unread = 0;
232 self.num_notifications = 0;
233 self.num_mentions = 0;
234 }
235
236 #[instrument(skip_all)]
239 fn find_and_process_events<'a>(
240 &mut self,
241 receipt_event_id: &EventId,
242 user_id: &UserId,
243 events: impl IntoIterator<Item = &'a TimelineEvent>,
244 ) -> bool {
245 let mut counting_receipts = false;
246
247 for event in events {
248 if let Some(event_id) = event.event_id() {
252 if event_id == receipt_event_id {
253 trace!("Found the event the receipt was referring to! Starting to count.");
256 self.reset();
257 counting_receipts = true;
258 continue;
259 }
260 }
261
262 if counting_receipts {
263 self.process_event(event, user_id);
264 }
265 }
266
267 counting_receipts
268 }
269}
270
271pub trait PreviousEventsProvider: Send + Sync {
273 fn for_room(&self, room_id: &RoomId) -> Vector<TimelineEvent>;
276}
277
278impl PreviousEventsProvider for () {
279 fn for_room(&self, _: &RoomId) -> Vector<TimelineEvent> {
280 Vector::new()
281 }
282}
283
284struct ReceiptSelector {
287 event_id_to_pos: BTreeMap<OwnedEventId, usize>,
289 latest_event_with_receipt: Option<OwnedEventId>,
292 latest_event_pos: Option<usize>,
294}
295
296impl ReceiptSelector {
297 fn new(
298 all_events: &Vector<TimelineEvent>,
299 latest_active_receipt_event: Option<&EventId>,
300 ) -> Self {
301 let event_id_to_pos = Self::create_sync_index(all_events.iter());
302
303 let best_pos =
304 latest_active_receipt_event.and_then(|event_id| event_id_to_pos.get(event_id)).copied();
305
306 Self { latest_event_pos: best_pos, latest_event_with_receipt: None, event_id_to_pos }
311 }
312
313 fn create_sync_index<'a>(
316 events: impl Iterator<Item = &'a TimelineEvent> + 'a,
317 ) -> BTreeMap<OwnedEventId, usize> {
318 BTreeMap::from_iter(
320 events
321 .enumerate()
322 .filter_map(|(pos, event)| event.event_id().map(|event_id| (event_id, pos))),
323 )
324 }
325
326 #[instrument(skip(self), fields(prev_pos = ?self.latest_event_pos, prev_receipt = ?self.latest_event_with_receipt))]
328 fn try_select_later(&mut self, event_id: &EventId, event_pos: usize) {
329 if let Some(best_pos) = self.latest_event_pos.as_mut() {
332 if event_pos >= *best_pos {
336 *best_pos = event_pos;
337 self.latest_event_with_receipt = Some(event_id.to_owned());
338 debug!("saving better");
339 } else {
340 trace!("not better, keeping previous");
341 }
342 } else {
343 self.latest_event_pos = Some(event_pos);
346 self.latest_event_with_receipt = Some(event_id.to_owned());
347 debug!("saving for the first time");
348 }
349 }
350
351 #[instrument(skip_all)]
353 fn handle_pending_receipts(&mut self, pending: &mut RingBuffer<OwnedEventId>) {
354 pending.retain(|event_id| {
356 if let Some(event_pos) = self.event_id_to_pos.get(event_id) {
357 trace!(%event_id, "matching event against its stashed receipt");
359 self.try_select_later(event_id, *event_pos);
360
361 false
364 } else {
365 true
367 }
368 });
369 }
370
371 #[instrument(skip_all)]
381 fn handle_new_receipt(
382 &mut self,
383 user_id: &UserId,
384 receipt_event: &ReceiptEventContent,
385 ) -> Vec<OwnedEventId> {
386 let mut pending = Vec::new();
387 for (event_id, receipts) in &receipt_event.0 {
389 for ty in [ReceiptType::Read, ReceiptType::ReadPrivate] {
390 if let Some(receipt) = receipts.get(&ty).and_then(|receipts| receipts.get(user_id))
391 {
392 if matches!(receipt.thread, ReceiptThread::Main | ReceiptThread::Unthreaded) {
393 trace!(%event_id, "found new candidate");
394 if let Some(event_pos) = self.event_id_to_pos.get(event_id) {
395 self.try_select_later(event_id, *event_pos);
396 } else {
397 trace!(%event_id, "stashed as pending");
399 pending.push(event_id.clone());
400 }
401 }
402 }
403 }
404 }
405 pending
406 }
407
408 #[instrument(skip_all)]
411 fn try_match_implicit(&mut self, user_id: &UserId, new_events: &[TimelineEvent]) {
412 for ev in new_events {
413 let Ok(Some(sender)) = ev.raw().get_field::<OwnedUserId>("sender") else { continue };
415 if sender == user_id {
416 let Some(event_id) = ev.event_id() else { continue };
418 if let Some(event_pos) = self.event_id_to_pos.get(&event_id) {
419 trace!(%event_id, "found an implicit receipt candidate");
420 self.try_select_later(&event_id, *event_pos);
421 }
422 }
423 }
424 }
425
426 fn select(self) -> Option<LatestReadReceipt> {
431 self.latest_event_with_receipt.map(|event_id| LatestReadReceipt { event_id })
432 }
433}
434
435fn events_intersects<'a>(
438 previous_events: impl Iterator<Item = &'a TimelineEvent>,
439 new_events: &[TimelineEvent],
440) -> bool {
441 let previous_events_ids = BTreeSet::from_iter(previous_events.filter_map(|ev| ev.event_id()));
442 new_events
443 .iter()
444 .any(|ev| ev.event_id().is_some_and(|event_id| previous_events_ids.contains(&event_id)))
445}
446
447#[instrument(skip_all, fields(room_id = %room_id))]
456pub(crate) fn compute_unread_counts(
457 user_id: &UserId,
458 room_id: &RoomId,
459 receipt_event: Option<&ReceiptEventContent>,
460 previous_events: Vector<TimelineEvent>,
461 new_events: &[TimelineEvent],
462 read_receipts: &mut RoomReadReceipts,
463) {
464 debug!(?read_receipts, "Starting.");
465
466 let all_events = if events_intersects(previous_events.iter(), new_events) {
467 Vector::from_iter(new_events.iter().cloned())
473 } else {
474 let mut all_events = previous_events;
475 all_events.extend(new_events.iter().cloned());
476 all_events
477 };
478
479 let new_receipt = {
480 let mut selector = ReceiptSelector::new(
481 &all_events,
482 read_receipts.latest_active.as_ref().map(|receipt| &*receipt.event_id),
483 );
484 selector.try_match_implicit(user_id, new_events);
485 selector.handle_pending_receipts(&mut read_receipts.pending);
486 if let Some(receipt_event) = receipt_event {
487 let new_pending = selector.handle_new_receipt(user_id, receipt_event);
488 if !new_pending.is_empty() {
489 read_receipts.pending.extend(new_pending);
490 }
491 }
492 selector.select()
493 };
494
495 if let Some(new_receipt) = new_receipt {
496 let event_id = new_receipt.event_id.clone();
502
503 trace!(%event_id, "Saving a new active read receipt");
505 read_receipts.latest_active = Some(new_receipt);
506
507 read_receipts.find_and_process_events(&event_id, user_id, all_events.iter());
510
511 debug!(?read_receipts, "after finding a better receipt");
512 return;
513 }
514
515 for event in new_events {
523 read_receipts.process_event(event, user_id);
524 }
525
526 debug!(?read_receipts, "no better receipt, {} new events", new_events.len());
527}
528
529fn marks_as_unread(event: &Raw<AnySyncTimelineEvent>, user_id: &UserId) -> bool {
531 let event = match event.deserialize() {
532 Ok(event) => event,
533 Err(err) => {
534 warn!(
535 "couldn't deserialize event {:?}: {err}",
536 event.get_field::<String>("event_id").ok().flatten()
537 );
538 return false;
539 }
540 };
541
542 if event.sender() == user_id {
543 return false;
545 }
546
547 match event {
548 AnySyncTimelineEvent::MessageLike(event) => {
549 let Some(content) = event.original_content() else {
551 tracing::trace!("not interesting because redacted");
552 return false;
553 };
554
555 if matches!(
557 content.relation(),
558 Some(ruma::events::room::encrypted::Relation::Replacement(..))
559 ) {
560 tracing::trace!("not interesting because edited");
561 return false;
562 }
563
564 match event {
565 AnySyncMessageLikeEvent::CallAnswer(_)
566 | AnySyncMessageLikeEvent::CallInvite(_)
567 | AnySyncMessageLikeEvent::CallNotify(_)
568 | AnySyncMessageLikeEvent::CallHangup(_)
569 | AnySyncMessageLikeEvent::CallCandidates(_)
570 | AnySyncMessageLikeEvent::CallNegotiate(_)
571 | AnySyncMessageLikeEvent::CallReject(_)
572 | AnySyncMessageLikeEvent::CallSelectAnswer(_)
573 | AnySyncMessageLikeEvent::PollResponse(_)
574 | AnySyncMessageLikeEvent::UnstablePollResponse(_)
575 | AnySyncMessageLikeEvent::Reaction(_)
576 | AnySyncMessageLikeEvent::RoomRedaction(_)
577 | AnySyncMessageLikeEvent::KeyVerificationStart(_)
578 | AnySyncMessageLikeEvent::KeyVerificationReady(_)
579 | AnySyncMessageLikeEvent::KeyVerificationCancel(_)
580 | AnySyncMessageLikeEvent::KeyVerificationAccept(_)
581 | AnySyncMessageLikeEvent::KeyVerificationDone(_)
582 | AnySyncMessageLikeEvent::KeyVerificationMac(_)
583 | AnySyncMessageLikeEvent::KeyVerificationKey(_) => false,
584
585 AnySyncMessageLikeEvent::PollStart(SyncMessageLikeEvent::Original(
587 OriginalSyncMessageLikeEvent {
588 content:
589 PollStartEventContent { relates_to: Some(Relation::Replacement(_)), .. },
590 ..
591 },
592 ))
593 | AnySyncMessageLikeEvent::UnstablePollStart(SyncMessageLikeEvent::Original(
594 OriginalSyncMessageLikeEvent {
595 content: UnstablePollStartEventContent::Replacement(_),
596 ..
597 },
598 )) => false,
599
600 AnySyncMessageLikeEvent::Message(_)
601 | AnySyncMessageLikeEvent::PollStart(_)
602 | AnySyncMessageLikeEvent::UnstablePollStart(_)
603 | AnySyncMessageLikeEvent::PollEnd(_)
604 | AnySyncMessageLikeEvent::UnstablePollEnd(_)
605 | AnySyncMessageLikeEvent::RoomEncrypted(_)
606 | AnySyncMessageLikeEvent::RoomMessage(_)
607 | AnySyncMessageLikeEvent::Sticker(_) => true,
608
609 _ => {
610 warn!("unhandled timeline event type: {}", event.event_type());
612 false
613 }
614 }
615 }
616
617 AnySyncTimelineEvent::State(_) => false,
618 }
619}
620
621#[cfg(test)]
622mod tests {
623 use std::{num::NonZeroUsize, ops::Not as _};
624
625 use eyeball_im::Vector;
626 use matrix_sdk_common::{deserialized_responses::TimelineEvent, ring_buffer::RingBuffer};
627 use matrix_sdk_test::event_factory::EventFactory;
628 use ruma::{
629 event_id,
630 events::{
631 receipt::{ReceiptThread, ReceiptType},
632 room::{member::MembershipState, message::MessageType},
633 },
634 owned_event_id, owned_user_id,
635 push::Action,
636 room_id, user_id, EventId, UserId,
637 };
638
639 use super::compute_unread_counts;
640 use crate::read_receipts::{marks_as_unread, ReceiptSelector, RoomReadReceipts};
641
642 #[test]
643 fn test_room_message_marks_as_unread() {
644 let user_id = user_id!("@alice:example.org");
645 let other_user_id = user_id!("@bob:example.org");
646
647 let f = EventFactory::new();
648
649 let ev = f.text_msg("A").event_id(event_id!("$ida")).sender(other_user_id).into_raw_sync();
651 assert!(marks_as_unread(&ev, user_id));
652
653 let ev = f.text_msg("A").event_id(event_id!("$ida")).sender(user_id).into_raw_sync();
655 assert!(marks_as_unread(&ev, user_id).not());
656 }
657
658 #[test]
659 fn test_room_edit_doesnt_mark_as_unread() {
660 let user_id = user_id!("@alice:example.org");
661 let other_user_id = user_id!("@bob:example.org");
662
663 let ev = EventFactory::new()
665 .text_msg("* edited message")
666 .edit(
667 event_id!("$someeventid:localhost"),
668 MessageType::text_plain("edited message").into(),
669 )
670 .event_id(event_id!("$ida"))
671 .sender(other_user_id)
672 .into_raw_sync();
673
674 assert!(marks_as_unread(&ev, user_id).not());
675 }
676
677 #[test]
678 fn test_redaction_doesnt_mark_room_as_unread() {
679 let user_id = user_id!("@alice:example.org");
680 let other_user_id = user_id!("@bob:example.org");
681
682 let ev = EventFactory::new()
684 .redaction(event_id!("$151957878228ssqrj:localhost"))
685 .sender(other_user_id)
686 .event_id(event_id!("$151957878228ssqrJ:localhost"))
687 .into_raw_sync();
688
689 assert!(marks_as_unread(&ev, user_id).not());
690 }
691
692 #[test]
693 fn test_reaction_doesnt_mark_room_as_unread() {
694 let user_id = user_id!("@alice:example.org");
695 let other_user_id = user_id!("@bob:example.org");
696
697 let ev = EventFactory::new()
699 .reaction(event_id!("$15275047031IXQRj:localhost"), "👍")
700 .sender(other_user_id)
701 .event_id(event_id!("$15275047031IXQRi:localhost"))
702 .into_raw_sync();
703
704 assert!(marks_as_unread(&ev, user_id).not());
705 }
706
707 #[test]
708 fn test_state_event_doesnt_mark_as_unread() {
709 let user_id = user_id!("@alice:example.org");
710 let event_id = event_id!("$1");
711
712 let ev = EventFactory::new()
713 .member(user_id)
714 .membership(MembershipState::Join)
715 .display_name("Alice")
716 .event_id(event_id)
717 .into_raw_sync();
718 assert!(marks_as_unread(&ev, user_id).not());
719
720 let other_user_id = user_id!("@bob:example.org");
721 assert!(marks_as_unread(&ev, other_user_id).not());
722 }
723
724 #[test]
725 fn test_count_unread_and_mentions() {
726 fn make_event(user_id: &UserId, push_actions: Vec<Action>) -> TimelineEvent {
727 let mut ev = EventFactory::new()
728 .text_msg("A")
729 .sender(user_id)
730 .event_id(event_id!("$ida"))
731 .into_event();
732 ev.push_actions = Some(push_actions);
733 ev
734 }
735
736 let user_id = user_id!("@alice:example.org");
737
738 let event = make_event(user_id, Vec::new());
740 let mut receipts = RoomReadReceipts::default();
741 receipts.process_event(&event, user_id);
742 assert_eq!(receipts.num_unread, 0);
743 assert_eq!(receipts.num_mentions, 0);
744 assert_eq!(receipts.num_notifications, 0);
745
746 let event = make_event(user_id!("@bob:example.org"), Vec::new());
748 let mut receipts = RoomReadReceipts::default();
749 receipts.process_event(&event, user_id);
750 assert_eq!(receipts.num_unread, 1);
751 assert_eq!(receipts.num_mentions, 0);
752 assert_eq!(receipts.num_notifications, 0);
753
754 let event = make_event(user_id!("@bob:example.org"), vec![Action::Notify]);
756 let mut receipts = RoomReadReceipts::default();
757 receipts.process_event(&event, user_id);
758 assert_eq!(receipts.num_unread, 1);
759 assert_eq!(receipts.num_mentions, 0);
760 assert_eq!(receipts.num_notifications, 1);
761
762 let event = make_event(
763 user_id!("@bob:example.org"),
764 vec![Action::SetTweak(ruma::push::Tweak::Highlight(true))],
765 );
766 let mut receipts = RoomReadReceipts::default();
767 receipts.process_event(&event, user_id);
768 assert_eq!(receipts.num_unread, 1);
769 assert_eq!(receipts.num_mentions, 1);
770 assert_eq!(receipts.num_notifications, 0);
771
772 let event = make_event(
773 user_id!("@bob:example.org"),
774 vec![Action::SetTweak(ruma::push::Tweak::Highlight(true)), Action::Notify],
775 );
776 let mut receipts = RoomReadReceipts::default();
777 receipts.process_event(&event, user_id);
778 assert_eq!(receipts.num_unread, 1);
779 assert_eq!(receipts.num_mentions, 1);
780 assert_eq!(receipts.num_notifications, 1);
781
782 let event = make_event(user_id!("@bob:example.org"), vec![Action::Notify, Action::Notify]);
785 let mut receipts = RoomReadReceipts::default();
786 receipts.process_event(&event, user_id);
787 assert_eq!(receipts.num_unread, 1);
788 assert_eq!(receipts.num_mentions, 0);
789 assert_eq!(receipts.num_notifications, 1);
790 }
791
792 #[test]
793 fn test_find_and_process_events() {
794 let ev0 = event_id!("$0");
795 let user_id = user_id!("@alice:example.org");
796
797 let mut receipts = RoomReadReceipts::default();
800 assert!(receipts.find_and_process_events(ev0, user_id, &[]).not());
801 assert_eq!(receipts.num_unread, 0);
802 assert_eq!(receipts.num_notifications, 0);
803 assert_eq!(receipts.num_mentions, 0);
804
805 fn make_event(event_id: &EventId) -> TimelineEvent {
808 EventFactory::new()
809 .text_msg("A")
810 .sender(user_id!("@bob:example.org"))
811 .event_id(event_id)
812 .into()
813 }
814
815 let mut receipts = RoomReadReceipts {
816 num_unread: 42,
817 num_notifications: 13,
818 num_mentions: 37,
819 ..Default::default()
820 };
821 assert!(receipts
822 .find_and_process_events(ev0, user_id, &[make_event(event_id!("$1"))],)
823 .not());
824 assert_eq!(receipts.num_unread, 42);
825 assert_eq!(receipts.num_notifications, 13);
826 assert_eq!(receipts.num_mentions, 37);
827
828 let mut receipts = RoomReadReceipts {
832 num_unread: 42,
833 num_notifications: 13,
834 num_mentions: 37,
835 ..Default::default()
836 };
837 assert!(receipts.find_and_process_events(ev0, user_id, &[make_event(ev0)]));
838 assert_eq!(receipts.num_unread, 0);
839 assert_eq!(receipts.num_notifications, 0);
840 assert_eq!(receipts.num_mentions, 0);
841
842 let mut receipts = RoomReadReceipts {
845 num_unread: 42,
846 num_notifications: 13,
847 num_mentions: 37,
848 ..Default::default()
849 };
850 assert!(receipts
851 .find_and_process_events(
852 ev0,
853 user_id,
854 &[
855 make_event(event_id!("$1")),
856 make_event(event_id!("$2")),
857 make_event(event_id!("$3"))
858 ],
859 )
860 .not());
861 assert_eq!(receipts.num_unread, 42);
862 assert_eq!(receipts.num_notifications, 13);
863 assert_eq!(receipts.num_mentions, 37);
864
865 let mut receipts = RoomReadReceipts {
868 num_unread: 42,
869 num_notifications: 13,
870 num_mentions: 37,
871 ..Default::default()
872 };
873 assert!(receipts.find_and_process_events(
874 ev0,
875 user_id,
876 &[
877 make_event(event_id!("$1")),
878 make_event(ev0),
879 make_event(event_id!("$2")),
880 make_event(event_id!("$3"))
881 ],
882 ));
883 assert_eq!(receipts.num_unread, 2);
884 assert_eq!(receipts.num_notifications, 0);
885 assert_eq!(receipts.num_mentions, 0);
886
887 let mut receipts = RoomReadReceipts {
889 num_unread: 42,
890 num_notifications: 13,
891 num_mentions: 37,
892 ..Default::default()
893 };
894 assert!(receipts.find_and_process_events(
895 ev0,
896 user_id,
897 &[
898 make_event(ev0),
899 make_event(event_id!("$1")),
900 make_event(ev0),
901 make_event(event_id!("$2")),
902 make_event(event_id!("$3"))
903 ],
904 ));
905 assert_eq!(receipts.num_unread, 2);
906 assert_eq!(receipts.num_notifications, 0);
907 assert_eq!(receipts.num_mentions, 0);
908 }
909
910 #[test]
912 fn test_basic_compute_unread_counts() {
913 let user_id = user_id!("@alice:example.org");
914 let other_user_id = user_id!("@bob:example.org");
915 let room_id = room_id!("!room:example.org");
916 let receipt_event_id = event_id!("$1");
917
918 let mut previous_events = Vector::new();
919
920 let f = EventFactory::new();
921 let ev1 = f.text_msg("A").sender(other_user_id).event_id(receipt_event_id).into_event();
922 let ev2 = f.text_msg("A").sender(other_user_id).event_id(event_id!("$2")).into_event();
923
924 let receipt_event = f
925 .read_receipts()
926 .add(receipt_event_id, user_id, ReceiptType::Read, ReceiptThread::Unthreaded)
927 .build();
928
929 let mut read_receipts = Default::default();
930 compute_unread_counts(
931 user_id,
932 room_id,
933 Some(&receipt_event),
934 previous_events.clone(),
935 &[ev1.clone(), ev2.clone()],
936 &mut read_receipts,
937 );
938
939 assert_eq!(read_receipts.num_unread, 1);
941
942 previous_events.push_back(ev1);
944 previous_events.push_back(ev2);
945
946 let new_event =
947 f.text_msg("A").sender(other_user_id).event_id(event_id!("$3")).into_event();
948 compute_unread_counts(
949 user_id,
950 room_id,
951 Some(&receipt_event),
952 previous_events,
953 &[new_event],
954 &mut read_receipts,
955 );
956
957 assert_eq!(read_receipts.num_unread, 2);
959 }
960
961 fn make_test_events(user_id: &UserId) -> Vector<TimelineEvent> {
962 let f = EventFactory::new().sender(user_id);
963 let ev1 = f.text_msg("With the lights out, it's less dangerous").event_id(event_id!("$1"));
964 let ev2 = f.text_msg("Here we are now, entertain us").event_id(event_id!("$2"));
965 let ev3 = f.text_msg("I feel stupid and contagious").event_id(event_id!("$3"));
966 let ev4 = f.text_msg("Here we are now, entertain us").event_id(event_id!("$4"));
967 let ev5 = f.text_msg("Hello, hello, hello, how low?").event_id(event_id!("$5"));
968 [ev1, ev2, ev3, ev4, ev5].into_iter().map(Into::into).collect()
969 }
970
971 #[test]
974 fn test_compute_unread_counts_multiple_receipts_in_one_event() {
975 let user_id = user_id!("@alice:example.org");
976 let room_id = room_id!("!room:example.org");
977
978 let all_events = make_test_events(user_id!("@bob:example.org"));
979 let head_events: Vector<_> = all_events.iter().take(2).cloned().collect();
980 let tail_events: Vec<_> = all_events.iter().skip(2).cloned().collect();
981
982 let f = EventFactory::new();
985 for receipt_type_1 in &[ReceiptType::Read, ReceiptType::ReadPrivate] {
986 for receipt_thread_1 in &[ReceiptThread::Unthreaded, ReceiptThread::Main] {
987 for receipt_type_2 in &[ReceiptType::Read, ReceiptType::ReadPrivate] {
988 for receipt_thread_2 in &[ReceiptThread::Unthreaded, ReceiptThread::Main] {
989 let receipt_event = f
990 .read_receipts()
991 .add(
992 event_id!("$2"),
993 user_id,
994 receipt_type_1.clone(),
995 receipt_thread_1.clone(),
996 )
997 .add(
998 event_id!("$3"),
999 user_id,
1000 receipt_type_2.clone(),
1001 receipt_thread_2.clone(),
1002 )
1003 .add(
1004 event_id!("$1"),
1005 user_id,
1006 receipt_type_1.clone(),
1007 receipt_thread_2.clone(),
1008 )
1009 .build();
1010
1011 let mut read_receipts = RoomReadReceipts::default();
1013
1014 compute_unread_counts(
1015 user_id,
1016 room_id,
1017 Some(&receipt_event),
1018 all_events.clone(),
1019 &[],
1020 &mut read_receipts,
1021 );
1022
1023 assert!(
1024 read_receipts != Default::default(),
1025 "read receipts have been updated"
1026 );
1027
1028 assert_eq!(read_receipts.num_unread, 2);
1030 assert_eq!(read_receipts.num_mentions, 0);
1031 assert_eq!(read_receipts.num_notifications, 0);
1032
1033 let mut read_receipts = RoomReadReceipts::default();
1035 compute_unread_counts(
1036 user_id,
1037 room_id,
1038 Some(&receipt_event),
1039 head_events.clone(),
1040 &tail_events,
1041 &mut read_receipts,
1042 );
1043
1044 assert!(
1045 read_receipts != Default::default(),
1046 "read receipts have been updated"
1047 );
1048
1049 assert_eq!(read_receipts.num_unread, 2);
1051 assert_eq!(read_receipts.num_mentions, 0);
1052 assert_eq!(read_receipts.num_notifications, 0);
1053 }
1054 }
1055 }
1056 }
1057 }
1058
1059 #[test]
1063 fn test_compute_unread_counts_updated_after_field_tracking() {
1064 let user_id = owned_user_id!("@alice:example.org");
1065 let room_id = room_id!("!room:example.org");
1066
1067 let events = make_test_events(user_id!("@bob:example.org"));
1068
1069 let receipt_event = EventFactory::new()
1070 .read_receipts()
1071 .add(event_id!("$6"), &user_id, ReceiptType::Read, ReceiptThread::Unthreaded)
1072 .build();
1073
1074 let mut read_receipts = RoomReadReceipts::default();
1075 assert!(read_receipts.pending.is_empty());
1076
1077 compute_unread_counts(
1080 &user_id,
1081 room_id,
1082 Some(&receipt_event),
1083 events,
1084 &[], &mut read_receipts,
1086 );
1087
1088 assert_eq!(read_receipts.num_unread, 0);
1090
1091 assert_eq!(read_receipts.pending.len(), 1);
1093 assert!(read_receipts.pending.iter().any(|ev| ev == event_id!("$6")));
1094 }
1095
1096 #[test]
1097 fn test_compute_unread_counts_limited_sync() {
1098 let user_id = owned_user_id!("@alice:example.org");
1099 let room_id = room_id!("!room:example.org");
1100
1101 let events = make_test_events(user_id!("@bob:example.org"));
1102
1103 let receipt_event = EventFactory::new()
1104 .read_receipts()
1105 .add(event_id!("$1"), &user_id, ReceiptType::Read, ReceiptThread::Unthreaded)
1106 .build();
1107
1108 let mut read_receipts = RoomReadReceipts::default();
1112 assert!(read_receipts.pending.is_empty());
1113
1114 let ev0 = events[0].clone();
1115
1116 compute_unread_counts(
1117 &user_id,
1118 room_id,
1119 Some(&receipt_event),
1120 events,
1121 &[ev0], &mut read_receipts,
1123 );
1124
1125 assert_eq!(read_receipts.num_unread, 0);
1127 assert!(read_receipts.pending.is_empty());
1128 }
1129
1130 #[test]
1131 fn test_receipt_selector_create_sync_index() {
1132 let uid = user_id!("@bob:example.org");
1133
1134 let events = make_test_events(uid);
1135
1136 let ev6 = EventFactory::new().text_msg("yolo").sender(uid).no_event_id().into_event();
1138
1139 let index = ReceiptSelector::create_sync_index(events.iter().chain(&[ev6]));
1140
1141 assert_eq!(*index.get(event_id!("$1")).unwrap(), 0);
1142 assert_eq!(*index.get(event_id!("$2")).unwrap(), 1);
1143 assert_eq!(*index.get(event_id!("$3")).unwrap(), 2);
1144 assert_eq!(*index.get(event_id!("$4")).unwrap(), 3);
1145 assert_eq!(*index.get(event_id!("$5")).unwrap(), 4);
1146 assert_eq!(index.get(event_id!("$6")), None);
1147
1148 assert_eq!(index.len(), 5);
1149
1150 let index = ReceiptSelector::create_sync_index(
1152 [events[1].clone(), events[2].clone(), events[4].clone()].iter(),
1153 );
1154
1155 assert_eq!(*index.get(event_id!("$2")).unwrap(), 0);
1156 assert_eq!(*index.get(event_id!("$3")).unwrap(), 1);
1157 assert_eq!(*index.get(event_id!("$5")).unwrap(), 2);
1158
1159 assert_eq!(index.len(), 3);
1160 }
1161
1162 #[test]
1163 fn test_receipt_selector_try_select_later() {
1164 let events = make_test_events(user_id!("@bob:example.org"));
1165
1166 {
1167 let mut selector = ReceiptSelector::new(&vec![].into(), None);
1169 selector.try_select_later(event_id!("$1"), 0);
1170 let best_receipt = selector.select();
1171 assert_eq!(best_receipt.unwrap().event_id, event_id!("$1"));
1172 }
1173
1174 {
1175 let mut selector = ReceiptSelector::new(&events, Some(event_id!("$3")));
1177 selector.try_select_later(event_id!("$1"), 0);
1178 let best_receipt = selector.select();
1179 assert!(best_receipt.is_none());
1180 }
1181
1182 {
1183 let mut selector = ReceiptSelector::new(&events, Some(event_id!("$1")));
1186 selector.try_select_later(event_id!("$1"), 0);
1187 let best_receipt = selector.select();
1188 assert_eq!(best_receipt.unwrap().event_id, event_id!("$1"));
1189 }
1190
1191 {
1192 let mut selector = ReceiptSelector::new(&events, Some(event_id!("$3")));
1194 selector.try_select_later(event_id!("$4"), 3);
1195 let best_receipt = selector.select();
1196 assert_eq!(best_receipt.unwrap().event_id, event_id!("$4"));
1197 }
1198 }
1199
1200 #[test]
1201 fn test_receipt_selector_handle_pending_receipts_noop() {
1202 let sender = user_id!("@bob:example.org");
1203 let f = EventFactory::new().sender(sender);
1204 let ev1 = f.text_msg("yo").event_id(event_id!("$1")).into_event();
1205 let ev2 = f.text_msg("well?").event_id(event_id!("$2")).into_event();
1206 let events: Vector<_> = vec![ev1, ev2].into();
1207
1208 {
1209 let mut selector = ReceiptSelector::new(&events, None);
1211
1212 let mut pending = RingBuffer::new(NonZeroUsize::new(16).unwrap());
1213 selector.handle_pending_receipts(&mut pending);
1214
1215 assert!(pending.is_empty());
1216
1217 let best_receipt = selector.select();
1218 assert!(best_receipt.is_none());
1219 }
1220
1221 {
1222 let mut selector = ReceiptSelector::new(&events, Some(event_id!("$1")));
1225
1226 let mut pending = RingBuffer::new(NonZeroUsize::new(16).unwrap());
1227 selector.handle_pending_receipts(&mut pending);
1228
1229 assert!(pending.is_empty());
1230
1231 let best_receipt = selector.select();
1232 assert!(best_receipt.is_none());
1233 }
1234 }
1235
1236 #[test]
1237 fn test_receipt_selector_handle_pending_receipts_doesnt_match_known_events() {
1238 let sender = user_id!("@bob:example.org");
1239 let f = EventFactory::new().sender(sender);
1240 let ev1 = f.text_msg("yo").event_id(event_id!("$1")).into_event();
1241 let ev2 = f.text_msg("well?").event_id(event_id!("$2")).into_event();
1242 let events: Vector<_> = vec![ev1, ev2].into();
1243
1244 {
1245 let mut selector = ReceiptSelector::new(&events, None);
1247
1248 let mut pending = RingBuffer::new(NonZeroUsize::new(16).unwrap());
1249 pending.push(owned_event_id!("$3"));
1250 selector.handle_pending_receipts(&mut pending);
1251
1252 assert_eq!(pending.len(), 1);
1253
1254 let best_receipt = selector.select();
1255 assert!(best_receipt.is_none());
1256 }
1257
1258 {
1259 let mut selector = ReceiptSelector::new(&events, Some(event_id!("$1")));
1261
1262 let mut pending = RingBuffer::new(NonZeroUsize::new(16).unwrap());
1263 pending.push(owned_event_id!("$3"));
1264 selector.handle_pending_receipts(&mut pending);
1265
1266 assert_eq!(pending.len(), 1);
1267
1268 let best_receipt = selector.select();
1269 assert!(best_receipt.is_none());
1270 }
1271 }
1272
1273 #[test]
1274 fn test_receipt_selector_handle_pending_receipts_matches_known_events_no_initial() {
1275 let sender = user_id!("@bob:example.org");
1276 let f = EventFactory::new().sender(sender);
1277 let ev1 = f.text_msg("yo").event_id(event_id!("$1")).into_event();
1278 let ev2 = f.text_msg("well?").event_id(event_id!("$2")).into_event();
1279 let events: Vector<_> = vec![ev1, ev2].into();
1280
1281 {
1282 let mut selector = ReceiptSelector::new(&events, None);
1284
1285 let mut pending = RingBuffer::new(NonZeroUsize::new(16).unwrap());
1286 pending.push(owned_event_id!("$2"));
1287 selector.handle_pending_receipts(&mut pending);
1288
1289 assert!(pending.is_empty());
1291
1292 let best_receipt = selector.select();
1294 assert_eq!(best_receipt.unwrap().event_id, event_id!("$2"));
1295 }
1296
1297 {
1298 let mut selector = ReceiptSelector::new(&events, None);
1300
1301 let mut pending = RingBuffer::new(NonZeroUsize::new(16).unwrap());
1302 pending.push(owned_event_id!("$1"));
1303 pending.push(owned_event_id!("$3"));
1304 selector.handle_pending_receipts(&mut pending);
1305
1306 assert_eq!(pending.len(), 1);
1308 assert!(pending.iter().any(|ev| ev == event_id!("$3")));
1309
1310 let best_receipt = selector.select();
1311 assert_eq!(best_receipt.unwrap().event_id, event_id!("$1"));
1312 }
1313 }
1314
1315 #[test]
1316 fn test_receipt_selector_handle_pending_receipts_matches_known_events_with_initial() {
1317 let sender = user_id!("@bob:example.org");
1318 let f = EventFactory::new().sender(sender);
1319 let ev1 = f.text_msg("yo").event_id(event_id!("$1")).into_event();
1320 let ev2 = f.text_msg("well?").event_id(event_id!("$2")).into_event();
1321 let events: Vector<_> = vec![ev1, ev2].into();
1322
1323 {
1324 let mut selector = ReceiptSelector::new(&events, Some(event_id!("$1")));
1327
1328 let mut pending = RingBuffer::new(NonZeroUsize::new(16).unwrap());
1329 pending.push(owned_event_id!("$2"));
1330 selector.handle_pending_receipts(&mut pending);
1331
1332 assert!(pending.is_empty());
1334
1335 let best_receipt = selector.select();
1337 assert_eq!(best_receipt.unwrap().event_id, event_id!("$2"));
1338 }
1339
1340 {
1341 let mut selector = ReceiptSelector::new(&events, Some(event_id!("$2")));
1343
1344 let mut pending = RingBuffer::new(NonZeroUsize::new(16).unwrap());
1345 pending.push(owned_event_id!("$1"));
1346 selector.handle_pending_receipts(&mut pending);
1347
1348 assert!(pending.is_empty());
1350
1351 let best_receipt = selector.select();
1352 assert!(best_receipt.is_none());
1353 }
1354 }
1355
1356 #[test]
1357 fn test_receipt_selector_handle_new_receipt() {
1358 let myself = user_id!("@alice:example.org");
1359 let events = make_test_events(user_id!("@bob:example.org"));
1360
1361 let f = EventFactory::new();
1362 {
1363 let mut selector = ReceiptSelector::new(&events, None);
1365
1366 let receipt_event = f
1367 .read_receipts()
1368 .add(
1369 event_id!("$5"),
1370 myself,
1371 ReceiptType::Read,
1372 ReceiptThread::Thread(owned_event_id!("$2")),
1373 )
1374 .build();
1375
1376 let pending = selector.handle_new_receipt(myself, &receipt_event);
1377 assert!(pending.is_empty());
1378
1379 let best_receipt = selector.select();
1380 assert!(best_receipt.is_none());
1381 }
1382
1383 for receipt_type in [ReceiptType::Read, ReceiptType::ReadPrivate] {
1384 for receipt_thread in [ReceiptThread::Main, ReceiptThread::Unthreaded] {
1385 {
1386 let mut selector = ReceiptSelector::new(&events, None);
1389
1390 let receipt_event = f
1391 .read_receipts()
1392 .add(event_id!("$6"), myself, receipt_type.clone(), receipt_thread.clone())
1393 .build();
1394
1395 let pending = selector.handle_new_receipt(myself, &receipt_event);
1396 assert_eq!(pending[0], event_id!("$6"));
1397 assert_eq!(pending.len(), 1);
1398
1399 let best_receipt = selector.select();
1400 assert!(best_receipt.is_none());
1401 }
1402
1403 {
1404 let mut selector = ReceiptSelector::new(&events, None);
1407
1408 let receipt_event = f
1409 .read_receipts()
1410 .add(event_id!("$3"), myself, receipt_type.clone(), receipt_thread.clone())
1411 .build();
1412
1413 let pending = selector.handle_new_receipt(myself, &receipt_event);
1414 assert!(pending.is_empty());
1415
1416 let best_receipt = selector.select();
1417 assert_eq!(best_receipt.unwrap().event_id, event_id!("$3"));
1418 }
1419
1420 {
1421 let mut selector = ReceiptSelector::new(&events, Some(event_id!("$4")));
1424
1425 let receipt_event = f
1426 .read_receipts()
1427 .add(event_id!("$3"), myself, receipt_type.clone(), receipt_thread.clone())
1428 .build();
1429
1430 let pending = selector.handle_new_receipt(myself, &receipt_event);
1431 assert!(pending.is_empty());
1432
1433 let best_receipt = selector.select();
1434 assert!(best_receipt.is_none());
1435 }
1436
1437 {
1438 let mut selector = ReceiptSelector::new(&events, Some(event_id!("$2")));
1441
1442 let receipt_event = f
1443 .read_receipts()
1444 .add(event_id!("$3"), myself, receipt_type.clone(), receipt_thread.clone())
1445 .build();
1446
1447 let pending = selector.handle_new_receipt(myself, &receipt_event);
1448 assert!(pending.is_empty());
1449
1450 let best_receipt = selector.select();
1451 assert_eq!(best_receipt.unwrap().event_id, event_id!("$3"));
1452 }
1453 }
1454 } {
1457 let mut selector = ReceiptSelector::new(&events, Some(event_id!("$2")));
1460
1461 let receipt_event = f
1462 .read_receipts()
1463 .add(event_id!("$4"), myself, ReceiptType::ReadPrivate, ReceiptThread::Unthreaded)
1464 .add(event_id!("$6"), myself, ReceiptType::ReadPrivate, ReceiptThread::Main)
1465 .add(event_id!("$3"), myself, ReceiptType::Read, ReceiptThread::Main)
1466 .build();
1467
1468 let pending = selector.handle_new_receipt(myself, &receipt_event);
1469 assert_eq!(pending.len(), 1);
1470 assert_eq!(pending[0], event_id!("$6"));
1471
1472 let best_receipt = selector.select();
1473 assert_eq!(best_receipt.unwrap().event_id, event_id!("$4"));
1474 }
1475 }
1476
1477 #[test]
1478 fn test_try_match_implicit() {
1479 let myself = owned_user_id!("@alice:example.org");
1480 let bob = user_id!("@bob:example.org");
1481
1482 let mut events = make_test_events(bob);
1483
1484 let mut selector = ReceiptSelector::new(&events, None);
1486 selector.try_match_implicit(&myself, &events.iter().cloned().collect::<Vec<_>>());
1488 let best_receipt = selector.select();
1490 assert!(best_receipt.is_none());
1491
1492 let f = EventFactory::new();
1494 events.push_back(
1495 f.text_msg("A mulatto, an albino")
1496 .sender(&myself)
1497 .event_id(event_id!("$6"))
1498 .into_event(),
1499 );
1500 events.push_back(
1501 f.text_msg("A mosquito, my libido").sender(bob).event_id(event_id!("$7")).into_event(),
1502 );
1503
1504 let mut selector = ReceiptSelector::new(&events, None);
1505 selector.try_match_implicit(&myself, &events.iter().cloned().collect::<Vec<_>>());
1507 let best_receipt = selector.select();
1509 assert_eq!(best_receipt.unwrap().event_id, event_id!("$6"));
1510 }
1511
1512 #[test]
1513 fn test_compute_unread_counts_with_implicit_receipt() {
1514 let user_id = user_id!("@alice:example.org");
1515 let bob = user_id!("@bob:example.org");
1516 let room_id = room_id!("!room:example.org");
1517
1518 let mut events = make_test_events(bob);
1520
1521 let f = EventFactory::new();
1523 events.push_back(
1524 f.text_msg("A mulatto, an albino")
1525 .sender(user_id)
1526 .event_id(event_id!("$6"))
1527 .into_event(),
1528 );
1529
1530 events.push_back(
1532 f.text_msg("A mosquito, my libido").sender(bob).event_id(event_id!("$7")).into_event(),
1533 );
1534 events.push_back(
1535 f.text_msg("A denial, a denial").sender(bob).event_id(event_id!("$8")).into_event(),
1536 );
1537
1538 let events: Vec<_> = events.into_iter().collect();
1539
1540 let receipt_event = f
1542 .read_receipts()
1543 .add(event_id!("$3"), user_id, ReceiptType::Read, ReceiptThread::Unthreaded)
1544 .build();
1545
1546 let mut read_receipts = RoomReadReceipts::default();
1547
1548 compute_unread_counts(
1551 user_id,
1552 room_id,
1553 Some(&receipt_event),
1554 Vector::new(),
1555 &events,
1556 &mut read_receipts,
1557 );
1558
1559 assert_eq!(read_receipts.num_unread, 2);
1561
1562 assert!(read_receipts.pending.is_empty());
1564
1565 assert_eq!(read_receipts.latest_active.unwrap().event_id, event_id!("$6"));
1567 }
1568}