matrix_sdk_ui/timeline/
pagination.rs1use async_rx::StreamExt as _;
16use async_stream::stream;
17use futures_core::Stream;
18use futures_util::{StreamExt as _, pin_mut};
19use matrix_sdk::event_cache::{PaginationStatus, RoomPagination};
20use tracing::instrument;
21
22use super::Error;
23use crate::timeline::{PaginationError::NotSupported, controller::TimelineFocusKind};
24
25impl super::Timeline {
26 #[instrument(skip_all, fields(room_id = ?self.room().room_id()))]
30 pub async fn paginate_backwards(&self, mut num_events: u16) -> Result<bool, Error> {
31 match self.controller.focus() {
32 TimelineFocusKind::Live { event_cache, .. } => {
33 match self.controller.live_lazy_paginate_backwards(num_events).await {
34 Some(needed_num_events) => {
35 num_events = needed_num_events.try_into().expect(
36 "failed to cast `needed_num_events` (`usize`) into `num_events` (`usize`)",
37 );
38 }
39 None => {
40 return Ok(false);
48 }
49 }
50
51 Ok(self.live_paginate_backwards(&event_cache.pagination(), num_events).await?)
52 }
53
54 TimelineFocusKind::Event { event_cache, .. } => {
55 Ok(event_cache.paginate_backwards(num_events).await?.hit_end_of_timeline)
56 }
57
58 TimelineFocusKind::Thread { event_cache, .. } => Ok(event_cache
59 .pagination()
60 .run_backwards_once(num_events)
61 .await
62 .map(|outcome| outcome.reached_start)?),
63
64 TimelineFocusKind::PinnedEvents { .. } => Err(Error::PaginationError(NotSupported)),
65 }
66 }
67
68 #[instrument(skip_all, fields(room_id = ?self.room().room_id()))]
72 pub async fn paginate_forwards(&self, num_events: u16) -> Result<bool, Error> {
73 match self.controller.focus() {
74 TimelineFocusKind::Live { .. } => Ok(true),
75
76 TimelineFocusKind::Event { event_cache, .. } => {
77 Ok(event_cache.paginate_forwards(num_events).await?.hit_end_of_timeline)
78 }
79
80 TimelineFocusKind::Thread { .. } | TimelineFocusKind::PinnedEvents { .. } => {
81 Err(Error::PaginationError(NotSupported))
82 }
83 }
84 }
85
86 async fn live_paginate_backwards(
93 &self,
94 event_cache_pagination: &RoomPagination,
95 batch_size: u16,
96 ) -> Result<bool, Error> {
97 loop {
98 match event_cache_pagination.run_backwards_once(batch_size).await {
99 Ok(outcome) => {
100 if outcome.reached_start {
101 self.controller.insert_timeline_start_if_missing().await;
102 return Ok(true);
103 }
104
105 if !outcome.events.is_empty() {
106 return Ok(false);
107 }
108
109 }
112
113 Err(err) => return Err(err.into()),
115 }
116 }
117 }
118
119 pub async fn live_back_pagination_status(
126 &self,
127 ) -> Option<(PaginationStatus, impl Stream<Item = PaginationStatus> + use<>)> {
128 let TimelineFocusKind::Live { event_cache, .. } = self.controller.focus() else {
129 return None;
130 };
131
132 let pagination = event_cache.pagination();
133
134 let mut status = pagination.status();
135
136 let current_value = self.controller.map_pagination_status(status.next_now()).await;
137
138 let controller = self.controller.clone();
139 let stream = Box::pin(stream! {
140 let status_stream = status.dedup();
141
142 pin_mut!(status_stream);
143
144 while let Some(state) = status_stream.next().await {
145 let state = controller.map_pagination_status(state).await;
146
147 match state {
148 PaginationStatus::Idle { hit_timeline_start } => {
149 if hit_timeline_start {
150 controller.insert_timeline_start_if_missing().await;
151 }
152 }
153 PaginationStatus::Paginating => {}
154 }
155
156 yield state;
157 }
158 });
159
160 Some((current_value, stream))
161 }
162}