matrix_sdk/paginators/
thread.rs1use std::{fmt::Formatter, future::Future, sync::Mutex};
20
21use matrix_sdk_base::{deserialized_responses::TimelineEvent, SendOutsideWasm, SyncOutsideWasm};
22use ruma::{api::Direction, OwnedEventId, UInt};
23
24use crate::{
25 paginators::{PaginationResult, PaginationToken, PaginatorError},
26 room::{IncludeRelations, Relations, RelationsOptions},
27 Error, Room,
28};
29
30pub trait PaginableThread: SendOutsideWasm + SyncOutsideWasm {
32 fn relations(
34 &self,
35 thread_root: OwnedEventId,
36 opts: RelationsOptions,
37 ) -> impl Future<Output = Result<Relations, Error>> + SendOutsideWasm;
38
39 fn load_event(
41 &self,
42 event_id: &OwnedEventId,
43 ) -> impl Future<Output = Result<TimelineEvent, Error>> + SendOutsideWasm;
44}
45
46impl PaginableThread for Room {
47 async fn relations(
48 &self,
49 thread_root: OwnedEventId,
50 opts: RelationsOptions,
51 ) -> Result<Relations, Error> {
52 self.relations(thread_root, opts).await
53 }
54
55 async fn load_event(&self, event_id: &OwnedEventId) -> Result<TimelineEvent, Error> {
56 self.event(event_id, None).await
57 }
58}
59
60pub struct ThreadedEventsLoader<P: PaginableThread> {
62 room: P,
64
65 root_event_id: OwnedEventId,
67
68 token: Mutex<PaginationToken>,
71}
72
73impl<P: PaginableThread> ThreadedEventsLoader<P> {
74 pub fn new(room: P, root_event_id: OwnedEventId) -> Self {
76 Self { room, root_event_id, token: Mutex::new(None.into()) }
77 }
78
79 pub async fn paginate_backwards(
85 &self,
86 num_events: UInt,
87 ) -> Result<PaginationResult, PaginatorError> {
88 let token = {
89 let token = self.token.lock().unwrap();
90
91 match &*token {
92 PaginationToken::None => None,
93 PaginationToken::HasMore(token) => Some(token.clone()),
94 PaginationToken::HitEnd => {
95 return Ok(PaginationResult { events: Vec::new(), hit_end_of_timeline: true });
96 }
97 }
98 };
99
100 let options = RelationsOptions {
101 from: token,
102 dir: Direction::Backward,
103 limit: Some(num_events),
104 include_relations: IncludeRelations::AllRelations,
105 recurse: true,
106 };
107
108 let mut result = self
109 .room
110 .relations(self.root_event_id.to_owned(), options)
111 .await
112 .map_err(|error| PaginatorError::SdkError(Box::new(error)))?;
113
114 let hit_end_of_timeline = result.next_batch_token.is_none();
115
116 {
118 let mut token = self.token.lock().unwrap();
119
120 *token = match result.next_batch_token {
121 Some(val) => PaginationToken::HasMore(val),
122 None => PaginationToken::HitEnd,
123 };
124 }
125
126 if hit_end_of_timeline {
128 let root_event = self
129 .room
130 .load_event(&self.root_event_id)
131 .await
132 .map_err(|err| PaginatorError::SdkError(Box::new(err)))?;
133
134 result.chunk.push(root_event);
135 }
136
137 Ok(PaginationResult { events: result.chunk, hit_end_of_timeline })
138 }
139}
140
141#[cfg(not(tarpaulin_include))]
142impl<P: PaginableThread> std::fmt::Debug for ThreadedEventsLoader<P> {
143 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
144 f.debug_struct("ThreadedEventsLoader").finish()
145 }
146}