ruma_client_api/threads/get_thread_subscriptions_changes.rs
1//! `GET /_matrix/client/*/thread_subscriptions`
2//!
3//! Retrieve a paginated range of thread subscriptions across all rooms.
4
5pub mod unstable {
6 //! `/unstable/` ([spec])
7 //!
8 //! [spec]: https://github.com/matrix-org/matrix-spec-proposals/pull/4308
9
10 use std::collections::BTreeMap;
11
12 use js_int::UInt;
13 use ruma_common::{
14 OwnedEventId, OwnedRoomId,
15 api::{Direction, auth_scheme::AccessToken, request, response},
16 metadata,
17 };
18 use serde::{Deserialize, Serialize};
19
20 metadata! {
21 method: GET,
22 rate_limited: true,
23 authentication: AccessToken,
24 history: {
25 unstable("org.matrix.msc4308") => "/_matrix/client/unstable/io.element.msc4308/thread_subscriptions",
26 }
27 }
28
29 /// Request type for the `get_thread_subscriptions_changes` endpoint.
30 #[request(error = crate::Error)]
31 pub struct Request {
32 /// The direction to use for pagination.
33 ///
34 /// Only `Direction::Backward` is meant to be supported, which is why this field is private
35 /// for now (as of 2025-08-21).
36 #[ruma_api(query)]
37 // Because this field is private, it is never read.
38 #[allow(dead_code)]
39 dir: Direction,
40
41 /// A token to continue pagination from.
42 ///
43 /// This token can be acquired from a previous `/thread_subscriptions` response, or the
44 /// `prev_batch` in a sliding sync response's `thread_subscriptions` field.
45 ///
46 /// The token is opaque and has no client-discernible meaning.
47 ///
48 /// If not provided, then the pagination starts from the "end".
49 #[ruma_api(query)]
50 #[serde(skip_serializing_if = "Option::is_none")]
51 pub from: Option<String>,
52
53 /// A token used to limit the pagination.
54 ///
55 /// The token can be set to the value of a sliding sync `pos` field used in a request that
56 /// returned new thread subscriptions with a `prev_batch` token.
57 #[ruma_api(query)]
58 #[serde(skip_serializing_if = "Option::is_none")]
59 pub to: Option<String>,
60
61 /// A maximum number of thread subscriptions to fetch in one response.
62 ///
63 /// Defaults to 100, if not provided. Servers may impose a smaller limit than requested.
64 #[ruma_api(query)]
65 #[serde(skip_serializing_if = "Option::is_none")]
66 pub limit: Option<UInt>,
67 }
68
69 /// A thread has been subscribed to at some point.
70 #[derive(Clone, Debug, Serialize, Deserialize)]
71 #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
72 pub struct ThreadSubscription {
73 /// Whether the subscription was made automatically by a client, not by manual user choice.
74 pub automatic: bool,
75
76 /// The bump stamp of the thread subscription, to be used to compare with other changes
77 /// related to the same thread.
78 pub bump_stamp: UInt,
79 }
80
81 impl ThreadSubscription {
82 /// Create a new [`ThreadSubscription`] with the given values.
83 pub fn new(automatic: bool, bump_stamp: UInt) -> Self {
84 Self { automatic, bump_stamp }
85 }
86 }
87
88 /// A thread has been unsubscribed to at some point.
89 #[derive(Clone, Debug, Serialize, Deserialize)]
90 #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
91 pub struct ThreadUnsubscription {
92 /// The bump stamp of the thread subscription, to be used to compare with other changes
93 /// related to the same thread.
94 pub bump_stamp: UInt,
95 }
96
97 impl ThreadUnsubscription {
98 /// Create a new [`ThreadUnsubscription`] with the given bump stamp.
99 pub fn new(bump_stamp: UInt) -> Self {
100 Self { bump_stamp }
101 }
102 }
103
104 /// Response type for the `get_thread_subscriptions_changes` endpoint.
105 #[response(error = crate::Error)]
106 pub struct Response {
107 /// New thread subscriptions.
108 #[serde(skip_serializing_if = "BTreeMap::is_empty")]
109 pub subscribed: BTreeMap<OwnedRoomId, BTreeMap<OwnedEventId, ThreadSubscription>>,
110
111 /// New thread unsubscriptions.
112 #[serde(skip_serializing_if = "BTreeMap::is_empty")]
113 pub unsubscribed: BTreeMap<OwnedRoomId, BTreeMap<OwnedEventId, ThreadUnsubscription>>,
114
115 /// If there are still more results to fetch, this is the token to use as the next `from`
116 /// value.
117 #[serde(skip_serializing_if = "Option::is_none")]
118 pub end: Option<String>,
119 }
120
121 impl Request {
122 /// Creates a new empty `Request`.
123 pub fn new() -> Self {
124 Self { dir: Direction::Backward, from: None, to: None, limit: None }
125 }
126 }
127
128 impl Response {
129 /// Creates a new empty `Response`.
130 pub fn new() -> Self {
131 Self { subscribed: BTreeMap::new(), unsubscribed: BTreeMap::new(), end: None }
132 }
133 }
134}