Implement num_live for sliding-sync room response.
Avoid using origin_server_ts for recency stamp. Signed-off-by: Jason Volk <jason@zemos.net>
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
mod v3;
|
mod v3;
|
||||||
mod v5;
|
mod v5;
|
||||||
|
|
||||||
use futures::{StreamExt, pin_mut};
|
use futures::{FutureExt, StreamExt, pin_mut};
|
||||||
use ruma::{RoomId, UserId};
|
use ruma::{RoomId, UserId};
|
||||||
use tuwunel_core::{
|
use tuwunel_core::{
|
||||||
Error, PduCount, Result,
|
Error, PduCount, Result,
|
||||||
@@ -42,10 +42,12 @@ async fn load_timeline(
|
|||||||
.by_ref()
|
.by_ref()
|
||||||
.take(limit)
|
.take(limit)
|
||||||
.collect()
|
.collect()
|
||||||
|
.map(|mut pdus: Vec<_>| {
|
||||||
|
pdus.reverse();
|
||||||
|
pdus
|
||||||
|
})
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let timeline_pdus: Vec<_> = timeline_pdus.into_iter().rev().collect();
|
|
||||||
|
|
||||||
// They /sync response doesn't always return all messages, so we say the output
|
// They /sync response doesn't always return all messages, so we say the output
|
||||||
// is limited unless there are events in non_timeline_pdus
|
// is limited unless there are events in non_timeline_pdus
|
||||||
let limited = non_timeline_pdus.next().await.is_some();
|
let limited = non_timeline_pdus.next().await.is_some();
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use futures::{
|
|||||||
future::{OptionFuture, join, join3, join4},
|
future::{OptionFuture, join, join3, join4},
|
||||||
};
|
};
|
||||||
use ruma::{
|
use ruma::{
|
||||||
JsOption, MxcUri, OwnedMxcUri, RoomId, UInt, UserId,
|
JsOption, MxcUri, OwnedMxcUri, RoomId, UserId,
|
||||||
api::client::sync::sync_events::{
|
api::client::sync::sync_events::{
|
||||||
UnreadNotificationsCount,
|
UnreadNotificationsCount,
|
||||||
v5::{DisplayName, response, response::Heroes},
|
v5::{DisplayName, response, response::Heroes},
|
||||||
@@ -19,7 +19,7 @@ use ruma::{
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
use tuwunel_core::{
|
use tuwunel_core::{
|
||||||
Result, at, debug_error, err, is_equal_to,
|
Result, at, err, is_equal_to,
|
||||||
matrix::{Event, StateKey, pdu::PduCount},
|
matrix::{Event, StateKey, pdu::PduCount},
|
||||||
ref_at,
|
ref_at,
|
||||||
utils::{
|
utils::{
|
||||||
@@ -49,7 +49,10 @@ pub(super) async fn handle(
|
|||||||
lists, membership, room_id, last_count, ..
|
lists, membership, room_id, last_count, ..
|
||||||
}: &WindowRoom,
|
}: &WindowRoom,
|
||||||
) -> Result<Option<response::Room>> {
|
) -> Result<Option<response::Room>> {
|
||||||
debug_assert!(DEFAULT_BUMP_TYPES.is_sorted(), "DEFAULT_BUMP_TYPES is not sorted");
|
debug_assert!(
|
||||||
|
DEFAULT_BUMP_TYPES.is_sorted(),
|
||||||
|
"DEFAULT_BUMP_TYPES must be sorted for binary search"
|
||||||
|
);
|
||||||
|
|
||||||
let &Room { roomsince, .. } = conn
|
let &Room { roomsince, .. } = conn
|
||||||
.rooms
|
.rooms
|
||||||
@@ -57,7 +60,7 @@ pub(super) async fn handle(
|
|||||||
.ok_or_else(|| err!("Missing connection state for {room_id}"))?;
|
.ok_or_else(|| err!("Missing connection state for {room_id}"))?;
|
||||||
|
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
roomsince == 0 || *last_count > roomsince,
|
*last_count > roomsince || *last_count == 0 || roomsince == 0,
|
||||||
"Stale room shouldn't be in the window"
|
"Stale room shouldn't be in the window"
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -91,13 +94,10 @@ pub(super) async fn handle(
|
|||||||
})
|
})
|
||||||
.into();
|
.into();
|
||||||
|
|
||||||
let Ok(timeline) = timeline.await.transpose() else {
|
let (timeline_pdus, limited, _lastcount) = timeline
|
||||||
debug_error!(?room_id, "Missing timeline.");
|
.await
|
||||||
return Ok(None);
|
.flat_ok()
|
||||||
};
|
.unwrap_or_else(|| (Vec::new(), true, PduCount::default()));
|
||||||
|
|
||||||
let (timeline_pdus, limited, _lastcount) =
|
|
||||||
timeline.unwrap_or_else(|| (Vec::new(), true, PduCount::default()));
|
|
||||||
|
|
||||||
let prev_batch = timeline_pdus
|
let prev_batch = timeline_pdus
|
||||||
.first()
|
.first()
|
||||||
@@ -114,14 +114,25 @@ pub(super) async fn handle(
|
|||||||
.binary_search(pdu.event_type())
|
.binary_search(pdu.event_type())
|
||||||
.is_ok()
|
.is_ok()
|
||||||
})
|
})
|
||||||
.fold(Option::<UInt>::None, |mut bump_stamp, (_, pdu)| {
|
.map(at!(0))
|
||||||
let ts = pdu.origin_server_ts();
|
.filter(|count| matches!(count, PduCount::Normal(_)))
|
||||||
if bump_stamp.is_none_or(|bump_stamp| bump_stamp < ts.get()) {
|
.map(PduCount::into_unsigned)
|
||||||
bump_stamp.replace(ts.get());
|
.max()
|
||||||
}
|
.map(TryInto::try_into)
|
||||||
|
.flat_ok();
|
||||||
|
|
||||||
bump_stamp
|
let num_live: OptionFuture<_> = roomsince
|
||||||
});
|
.ne(&0)
|
||||||
|
.and_is(limited || timeline_pdus.len() >= timeline_limit)
|
||||||
|
.then(|| {
|
||||||
|
services
|
||||||
|
.timeline
|
||||||
|
.pdus(None, room_id, Some(roomsince.into()))
|
||||||
|
.count()
|
||||||
|
.map(TryInto::try_into)
|
||||||
|
.map(Result::ok)
|
||||||
|
})
|
||||||
|
.into();
|
||||||
|
|
||||||
let lazy = required_state
|
let lazy = required_state
|
||||||
.iter()
|
.iter()
|
||||||
@@ -141,6 +152,14 @@ pub(super) async fn handle(
|
|||||||
.map(|sender| (StateEventType::RoomMember, StateKey::from_str(sender.as_str())))
|
.map(|sender| (StateEventType::RoomMember, StateKey::from_str(sender.as_str())))
|
||||||
.stream();
|
.stream();
|
||||||
|
|
||||||
|
let timeline = timeline_pdus
|
||||||
|
.iter()
|
||||||
|
.stream()
|
||||||
|
.filter_map(|item| ignored_filter(services, item.clone(), sender_user))
|
||||||
|
.map(at!(1))
|
||||||
|
.map(Event::into_format)
|
||||||
|
.collect();
|
||||||
|
|
||||||
let wildcard_state = required_state
|
let wildcard_state = required_state
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|(_, state_key)| state_key == "*")
|
.filter(|(_, state_key)| state_key == "*")
|
||||||
@@ -186,14 +205,6 @@ pub(super) async fn handle(
|
|||||||
})
|
})
|
||||||
.into();
|
.into();
|
||||||
|
|
||||||
let timeline = timeline_pdus
|
|
||||||
.iter()
|
|
||||||
.stream()
|
|
||||||
.filter_map(|item| ignored_filter(services, item.clone(), sender_user))
|
|
||||||
.map(at!(1))
|
|
||||||
.map(Event::into_format)
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let room_name = services
|
let room_name = services
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.get_name(room_id)
|
.get_name(room_id)
|
||||||
@@ -243,12 +254,12 @@ pub(super) async fn handle(
|
|||||||
.last_notification_read(sender_user, room_id);
|
.last_notification_read(sender_user, room_id);
|
||||||
|
|
||||||
let meta = join3(room_name, room_avatar, is_dm);
|
let meta = join3(room_name, room_avatar, is_dm);
|
||||||
let events = join3(timeline, required_state, invite_state);
|
let events = join4(timeline, num_live, required_state, invite_state);
|
||||||
let member_counts = join(joined_count, invited_count);
|
let member_counts = join(joined_count, invited_count);
|
||||||
let notification_counts = join3(highlight_count, notification_count, last_read_count);
|
let notification_counts = join3(highlight_count, notification_count, last_read_count);
|
||||||
let (
|
let (
|
||||||
(room_name, room_avatar, is_dm),
|
(room_name, room_avatar, is_dm),
|
||||||
(timeline, required_state, invite_state),
|
(timeline, num_live, required_state, invite_state),
|
||||||
(joined_count, invited_count),
|
(joined_count, invited_count),
|
||||||
(highlight_count, notification_count, _last_notification_read),
|
(highlight_count, notification_count, _last_notification_read),
|
||||||
) = join4(meta, events, member_counts, notification_counts)
|
) = join4(meta, events, member_counts, notification_counts)
|
||||||
@@ -264,23 +275,21 @@ pub(super) async fn handle(
|
|||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let num_live = None; // Count events in timeline greater than global sync counter
|
|
||||||
|
|
||||||
Ok(Some(response::Room {
|
Ok(Some(response::Room {
|
||||||
initial: Some(roomsince == 0),
|
initial: roomsince.eq(&0).then_some(true),
|
||||||
lists: lists.clone(),
|
lists: lists.clone(),
|
||||||
membership: membership.clone(),
|
membership: membership.clone(),
|
||||||
name: room_name.or(hero_name),
|
name: room_name.or(hero_name),
|
||||||
avatar: JsOption::from_option(room_avatar.or(heroes_avatar)),
|
avatar: JsOption::from_option(room_avatar.or(heroes_avatar)),
|
||||||
is_dm,
|
is_dm,
|
||||||
|
heroes,
|
||||||
required_state,
|
required_state,
|
||||||
invite_state: invite_state.flatten(),
|
invite_state: invite_state.flatten(),
|
||||||
prev_batch: prev_batch.as_deref().map(Into::into),
|
prev_batch: prev_batch.as_deref().map(Into::into),
|
||||||
|
num_live: num_live.flatten(),
|
||||||
limited,
|
limited,
|
||||||
timeline,
|
timeline,
|
||||||
bump_stamp,
|
bump_stamp,
|
||||||
heroes,
|
|
||||||
num_live,
|
|
||||||
joined_count,
|
joined_count,
|
||||||
invited_count,
|
invited_count,
|
||||||
unread_notifications: UnreadNotificationsCount { highlight_count, notification_count },
|
unread_notifications: UnreadNotificationsCount { highlight_count, notification_count },
|
||||||
|
|||||||
Reference in New Issue
Block a user