Skip to main content

tuwunel_api_client/read_marker/
read_markers.rs

1use std::collections::BTreeMap;
2
3use axum::extract::State;
4use ruma::{
5	MilliSecondsSinceUnixEpoch,
6	api::client::read_marker::set_read_marker,
7	events::receipt::{Receipt, ReceiptEvent, ReceiptEventContent, ReceiptThread, ReceiptType},
8};
9use tuwunel_core::Result;
10use tuwunel_service::presence::Ping;
11
12use super::{
13	ensure_receipt_visible, reset_and_refresh_badge, set_fully_read, set_private_marker,
14};
15use crate::{ClientIp, Ruma};
16
17/// # `POST /_matrix/client/r0/rooms/{roomId}/read_markers`
18///
19/// Sets different types of read markers.
20///
21/// - Updates fully-read account data event to `fully_read`
22/// - If `read_receipt` is set: Update private marker and public read receipt
23///   EDU
24pub(crate) async fn set_read_marker_route(
25	State(services): State<crate::State>,
26	ClientIp(client): ClientIp,
27	body: Ruma<set_read_marker::v3::Request>,
28) -> Result<set_read_marker::v3::Response> {
29	let sender_user = body.sender_user();
30
31	if let Some(event) = &body.private_read_receipt {
32		ensure_receipt_visible(&services, &body.room_id, sender_user, event).await?;
33	}
34
35	if let Some(event) = &body.read_receipt {
36		ensure_receipt_visible(&services, &body.room_id, sender_user, event).await?;
37	}
38
39	if let Some(event) = &body.fully_read {
40		set_fully_read(&services, &body.room_id, sender_user, event, body.allow_backward).await?;
41	}
42
43	let private_advanced = match &body.private_read_receipt {
44		| None => false,
45		| Some(event) =>
46			set_private_marker(
47				&services,
48				&body.room_id,
49				sender_user,
50				event,
51				&ReceiptThread::Unthreaded,
52			)
53			.await?,
54	};
55
56	let public_advanced = match &body.read_receipt {
57		| None => false,
58		| Some(event) => {
59			let receipt_content = BTreeMap::from_iter([(
60				event.to_owned(),
61				BTreeMap::from_iter([(
62					ReceiptType::Read,
63					BTreeMap::from_iter([(sender_user.to_owned(), Receipt {
64						ts: Some(MilliSecondsSinceUnixEpoch::now()),
65						thread: ReceiptThread::Unthreaded,
66					})]),
67				)]),
68			)]);
69
70			let advanced = services
71				.read_receipt
72				.readreceipt_update(sender_user, &body.room_id, &ReceiptEvent {
73					content: ReceiptEventContent(receipt_content),
74					room_id: body.room_id.clone(),
75				})
76				.await;
77
78			let ping = Ping {
79				device_id: body.sender_device.as_deref(),
80				client_ip: Some(client),
81				appservice: body.appservice_info.as_ref(),
82				..Default::default()
83			};
84
85			services
86				.presence
87				.maybe_ping_presence(sender_user, ping)
88				.await
89				.ok();
90
91			advanced
92		},
93	};
94
95	// Route through the dispatcher so per-thread counts are also cleared;
96	// `/read_markers` predates MSC3771 and carries no thread field.
97	if private_advanced || public_advanced {
98		reset_and_refresh_badge(
99			&services,
100			sender_user,
101			&body.room_id,
102			None,
103			&ReceiptThread::Unthreaded,
104		)
105		.await;
106	}
107
108	Ok(set_read_marker::v3::Response {})
109}