Skip to content

Commit

Permalink
Use timestamps for receipts, not event ids.
Browse files Browse the repository at this point in the history
  • Loading branch information
pkulak committed May 8, 2023
1 parent 52c3fb6 commit a96cab3
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 37 deletions.
2 changes: 1 addition & 1 deletion src/widgets/chat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -833,7 +833,7 @@ fn make_message_list(
}

// apply our read receipts
Message::apply_receipts(&mut messages, receipts);
Message::apply_receipts(&mut messages, &mut receipts.get_all());

// update senders to friendly names
messages.iter_mut().for_each(|m| m.update_senders(members));
Expand Down
30 changes: 25 additions & 5 deletions src/widgets/message.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use chrono::TimeZone;
use log::info;
use std::cell::Cell;
use std::collections::BinaryHeap;
use std::iter;
use std::time::{Duration, SystemTime};

Expand Down Expand Up @@ -27,7 +29,7 @@ use tui::style::{Color, Style};
use tui::text::{Span, Spans};
use tui::widgets::ListItem;

use super::receipts::Receipts;
use super::receipts::Receipt;

// A Message is a line in the chat window; what a user would generally
// consider a "message". It has reactions, edits, and is generally in a state
Expand Down Expand Up @@ -328,10 +330,28 @@ impl Message {
reply_result
}

pub fn apply_receipts(messages: &mut [Message], receipts: &Receipts) {
for message in messages.iter_mut() {
if let Some(usernames) = receipts.get(&message.id) {
message.receipts = usernames.clone()
/// Given a binary heap (priority queue) of Receipts, run through the
/// the messages, popping off receipts and attaching them. This way we
/// only show a single receipt per user, on the latest message they have
/// read.
pub fn apply_receipts(messages: &mut [Message], heap: &mut BinaryHeap<Receipt>) {
for message in messages.iter_mut().rev() {
Message::apply_receipts(&mut message.replies, heap);

loop {
if let Some(candidate) = heap.peek() {
if candidate.timestamp > &message.sent {
message
.receipts
.push(Username::new(candidate.user_id.clone()));

heap.pop();
} else {
break;
}
} else {
return;
}
}
}
}
Expand Down
73 changes: 42 additions & 31 deletions src/widgets/receipts.rs
Original file line number Diff line number Diff line change
@@ -1,59 +1,64 @@
use ruma::OwnedUserId;
use std::collections::HashMap;
use ruma::{MilliSecondsSinceUnixEpoch, OwnedUserId};
use std::collections::{btree_map::Entry, BTreeMap, BinaryHeap};

use ruma::{
events::receipt::{ReceiptEventContent, ReceiptType},
OwnedEventId,
};

use crate::matrix::username::Username;
use ruma::events::receipt::{ReceiptEventContent, ReceiptType};

/// A place to put and update read receipts.
pub struct Receipts {
events: HashMap<OwnedEventId, Vec<Username>>,
markers: BTreeMap<OwnedUserId, MilliSecondsSinceUnixEpoch>,
ignore: OwnedUserId,
}

impl Receipts {
pub fn new(ignore: OwnedUserId) -> Self {
Receipts {
events: HashMap::default(),
markers: BTreeMap::default(),
ignore,
}
}

pub fn get(&self, event_id: &OwnedEventId) -> Option<&Vec<Username>> {
self.events.get(event_id)
}

pub fn apply_event(&mut self, event: &ReceiptEventContent) {
for (event_id, types) in event.iter() {
for types in event.values() {
if let Some(user_ids) = types.get(&ReceiptType::Read) {
for user_id in user_ids.keys() {
self.apply_event_and_user(event_id, user_id);
for (user_id, receipt) in user_ids.iter() {
if let Some(ts) = &receipt.ts {
self.apply_timestamp_and_user(ts, user_id);
}
}
}
}
}

fn apply_event_and_user(&mut self, event_id: &OwnedEventId, user_id: &OwnedUserId) {
pub fn get_all(&self) -> BinaryHeap<Receipt> {
let mut heap = BinaryHeap::with_capacity(self.markers.len());

heap.extend(self.markers.iter().map(|(k, v)| Receipt {
timestamp: v,
user_id: k,
}));

heap
}

fn apply_timestamp_and_user(
&mut self,
timestamp: &MilliSecondsSinceUnixEpoch,
user_id: &OwnedUserId,
) {
if user_id == &self.ignore {
return;
}

// wipe out any previous receipts for this user
for (_, usernames) in self.events.iter_mut() {
usernames.retain(|u| &u.id != user_id)
}

// add our receipt
self.events
.entry(event_id.clone())
.or_insert_with(|| Vec::with_capacity(1))
.push(Username::new(user_id.clone()));

// and clean up any now-empty vectors
self.events.retain(|_, value| !value.is_empty());
match self.markers.entry(user_id.clone()) {
Entry::Vacant(entry) => {
entry.insert(*timestamp);
}
Entry::Occupied(mut entry) => {
if timestamp > entry.get() {
*entry.get_mut() = *timestamp
}
}
};
}

pub fn get_senders(event: &ReceiptEventContent) -> Vec<&OwnedUserId> {
Expand All @@ -70,3 +75,9 @@ impl Receipts {
ids
}
}

#[derive(Eq, PartialEq, Ord, PartialOrd, Debug)]
pub struct Receipt<'a> {
pub timestamp: &'a MilliSecondsSinceUnixEpoch,
pub user_id: &'a OwnedUserId,
}

0 comments on commit a96cab3

Please sign in to comment.