improvement: bundle typing events and only send on changes
Fixes #67 and #49
This commit is contained in:
parent
8328eeb5ac
commit
168f2281fd
4 changed files with 141 additions and 98 deletions
|
@ -52,31 +52,6 @@ impl Rooms {
|
|||
.is_some())
|
||||
}
|
||||
|
||||
// TODO: Remove and replace with public room dir
|
||||
/// Returns a vector over all rooms.
|
||||
pub fn all_rooms(&self) -> Vec<RoomId> {
|
||||
let mut room_ids = self
|
||||
.roomid_pduleaves
|
||||
.iter()
|
||||
.keys()
|
||||
.map(|key| {
|
||||
RoomId::try_from(
|
||||
&*utils::string_from_bytes(
|
||||
&key.unwrap()
|
||||
.iter()
|
||||
.copied()
|
||||
.take_while(|&x| x != 0xff) // until delimiter
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
.unwrap()
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
room_ids.dedup();
|
||||
room_ids
|
||||
}
|
||||
|
||||
/// Returns the full room state.
|
||||
pub fn room_state(&self, room_id: &RoomId) -> Result<HashMap<(EventType, String), PduEvent>> {
|
||||
let mut hashmap = HashMap::new();
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
use crate::{utils, Result};
|
||||
use crate::{utils, Error, Result};
|
||||
use ruma_events::{collections::only::Event as EduEvent, EventJson};
|
||||
use ruma_identifiers::{RoomId, UserId};
|
||||
use std::convert::TryFrom;
|
||||
|
||||
pub struct RoomEdus {
|
||||
pub(in super::super) roomuserid_lastread: sled::Tree, // RoomUserId = Room + User
|
||||
pub(in super::super) roomlatestid_roomlatest: sled::Tree, // Read Receipts, RoomLatestId = RoomId + Count + UserId
|
||||
pub(in super::super) roomactiveid_roomactive: sled::Tree, // Typing, RoomActiveId = RoomId + TimeoutTime + Count
|
||||
pub(in super::super) roomactiveid_userid: sled::Tree, // Typing, RoomActiveId = RoomId + TimeoutTime + Count
|
||||
pub(in super::super) roomid_lastroomactiveupdate: sled::Tree, // LastRoomActiveUpdate = Count
|
||||
}
|
||||
|
||||
impl RoomEdus {
|
||||
|
@ -79,10 +81,11 @@ impl RoomEdus {
|
|||
.map(|(_, v)| Ok(serde_json::from_slice(&v)?)))
|
||||
}
|
||||
|
||||
/// Adds an event that will be saved until the `timeout` timestamp (e.g. typing notifications).
|
||||
/// Sets a user as typing until the timeout timestamp is reached or roomactive_remove is
|
||||
/// called.
|
||||
pub fn roomactive_add(
|
||||
&self,
|
||||
event: EduEvent,
|
||||
user_id: &UserId,
|
||||
room_id: &RoomId,
|
||||
timeout: u64,
|
||||
globals: &super::super::globals::Globals,
|
||||
|
@ -90,9 +93,73 @@ impl RoomEdus {
|
|||
let mut prefix = room_id.to_string().as_bytes().to_vec();
|
||||
prefix.push(0xff);
|
||||
|
||||
// Cleanup all outdated edus before inserting a new one
|
||||
let count = globals.next_count()?.to_be_bytes();
|
||||
|
||||
let mut room_active_id = prefix;
|
||||
room_active_id.extend_from_slice(&timeout.to_be_bytes());
|
||||
room_active_id.push(0xff);
|
||||
room_active_id.extend_from_slice(&count);
|
||||
|
||||
self.roomactiveid_userid
|
||||
.insert(&room_active_id, &*user_id.to_string().as_bytes())?;
|
||||
|
||||
self.roomid_lastroomactiveupdate
|
||||
.insert(&room_id.to_string().as_bytes(), &count)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Removes a user from typing before the timeout is reached.
|
||||
pub fn roomactive_remove(
|
||||
&self,
|
||||
user_id: &UserId,
|
||||
room_id: &RoomId,
|
||||
globals: &super::super::globals::Globals,
|
||||
) -> Result<()> {
|
||||
let mut prefix = room_id.to_string().as_bytes().to_vec();
|
||||
prefix.push(0xff);
|
||||
|
||||
let user_id = user_id.to_string();
|
||||
|
||||
let mut found_outdated = false;
|
||||
|
||||
// Maybe there are multiple ones from calling roomactive_add multiple times
|
||||
for outdated_edu in self
|
||||
.roomactiveid_roomactive
|
||||
.roomactiveid_userid
|
||||
.scan_prefix(&prefix)
|
||||
.filter_map(|r| r.ok())
|
||||
.filter(|(_, v)| v == user_id.as_bytes())
|
||||
{
|
||||
self.roomactiveid_userid.remove(outdated_edu.0)?;
|
||||
found_outdated = true;
|
||||
}
|
||||
|
||||
if found_outdated {
|
||||
self.roomid_lastroomactiveupdate.insert(
|
||||
&room_id.to_string().as_bytes(),
|
||||
&globals.next_count()?.to_be_bytes(),
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Makes sure that typing events with old timestamps get removed.
|
||||
fn roomactives_maintain(
|
||||
&self,
|
||||
room_id: &RoomId,
|
||||
globals: &super::super::globals::Globals,
|
||||
) -> Result<()> {
|
||||
let mut prefix = room_id.to_string().as_bytes().to_vec();
|
||||
prefix.push(0xff);
|
||||
|
||||
let current_timestamp = utils::millis_since_unix_epoch();
|
||||
|
||||
let mut found_outdated = false;
|
||||
|
||||
// Find all outdated edus before inserting a new one
|
||||
for outdated_edu in self
|
||||
.roomactiveid_userid
|
||||
.scan_prefix(&prefix)
|
||||
.keys()
|
||||
.filter_map(|r| r.ok())
|
||||
|
@ -101,60 +168,59 @@ impl RoomEdus {
|
|||
k.split(|&c| c == 0xff)
|
||||
.nth(1)
|
||||
.expect("roomactive has valid timestamp and delimiters"),
|
||||
) < utils::millis_since_unix_epoch()
|
||||
) < current_timestamp
|
||||
})
|
||||
{
|
||||
// This is an outdated edu (time > timestamp)
|
||||
self.roomlatestid_roomlatest.remove(outdated_edu)?;
|
||||
found_outdated = true;
|
||||
}
|
||||
|
||||
let mut room_active_id = prefix;
|
||||
room_active_id.extend_from_slice(&timeout.to_be_bytes());
|
||||
room_active_id.push(0xff);
|
||||
room_active_id.extend_from_slice(&globals.next_count()?.to_be_bytes());
|
||||
|
||||
self.roomactiveid_roomactive
|
||||
.insert(room_active_id, &*serde_json::to_string(&event)?)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Removes an active event manually (before the timeout is reached).
|
||||
pub fn roomactive_remove(&self, event: EduEvent, room_id: &RoomId) -> Result<()> {
|
||||
let mut prefix = room_id.to_string().as_bytes().to_vec();
|
||||
prefix.push(0xff);
|
||||
|
||||
let json = serde_json::to_string(&event)?;
|
||||
|
||||
// Remove outdated entries
|
||||
for outdated_edu in self
|
||||
.roomactiveid_roomactive
|
||||
.scan_prefix(&prefix)
|
||||
.filter_map(|r| r.ok())
|
||||
.filter(|(_, v)| v == json.as_bytes())
|
||||
{
|
||||
self.roomactiveid_roomactive.remove(outdated_edu.0)?;
|
||||
if found_outdated {
|
||||
self.roomid_lastroomactiveupdate.insert(
|
||||
&room_id.to_string().as_bytes(),
|
||||
&globals.next_count()?.to_be_bytes(),
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns an iterator over all active events (e.g. typing notifications).
|
||||
pub fn roomactives_all(
|
||||
pub fn last_roomactive_update(
|
||||
&self,
|
||||
room_id: &RoomId,
|
||||
) -> impl Iterator<Item = Result<EventJson<EduEvent>>> {
|
||||
globals: &super::super::globals::Globals,
|
||||
) -> Result<u64> {
|
||||
self.roomactives_maintain(room_id, globals)?;
|
||||
|
||||
Ok(self
|
||||
.roomid_lastroomactiveupdate
|
||||
.get(&room_id.to_string().as_bytes())?
|
||||
.map(|bytes| utils::u64_from_bytes(&bytes))
|
||||
.unwrap_or(0))
|
||||
}
|
||||
|
||||
/// Returns an iterator over all active events (e.g. typing notifications).
|
||||
pub fn roomactives_all(&self, room_id: &RoomId) -> Result<ruma_events::typing::TypingEvent> {
|
||||
let mut prefix = room_id.to_string().as_bytes().to_vec();
|
||||
prefix.push(0xff);
|
||||
|
||||
let mut first_active_edu = prefix.clone();
|
||||
first_active_edu.extend_from_slice(&utils::millis_since_unix_epoch().to_be_bytes());
|
||||
let mut user_ids = Vec::new();
|
||||
|
||||
self.roomactiveid_roomactive
|
||||
.range(first_active_edu..)
|
||||
.filter_map(|r| r.ok())
|
||||
.take_while(move |(k, _)| k.starts_with(&prefix))
|
||||
.map(|(_, v)| Ok(serde_json::from_slice(&v)?))
|
||||
for user_id in self
|
||||
.roomactiveid_userid
|
||||
.scan_prefix(prefix)
|
||||
.values()
|
||||
.map(|user_id| Ok::<_, Error>(UserId::try_from(utils::string_from_bytes(&user_id?)?)?))
|
||||
{
|
||||
user_ids.push(user_id?);
|
||||
}
|
||||
|
||||
Ok(ruma_events::typing::TypingEvent {
|
||||
content: ruma_events::typing::TypingEventContent { user_ids },
|
||||
room_id: None, // Can be inferred
|
||||
})
|
||||
}
|
||||
|
||||
/// Sets a private read marker at `count`.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue