From 9359dc5be05bd2d7587fa5df4d3e755d61f5a8ff Mon Sep 17 00:00:00 2001 From: Pierre HUBERT Date: Mon, 1 Dec 2025 10:42:19 +0100 Subject: [PATCH] Send read receipts --- .../matrix/matrix_event_controller.rs | 22 ++++++++++++++++ matrixgw_backend/src/main.rs | 4 +++ .../src/api/matrix/MatrixApiEvent.ts | 10 ++++++++ .../src/widgets/messages/RoomWidget.tsx | 25 ++++++++++++++++++- 4 files changed, 60 insertions(+), 1 deletion(-) diff --git a/matrixgw_backend/src/controllers/matrix/matrix_event_controller.rs b/matrixgw_backend/src/controllers/matrix/matrix_event_controller.rs index efbe9e0..94e4eb1 100644 --- a/matrixgw_backend/src/controllers/matrix/matrix_event_controller.rs +++ b/matrixgw_backend/src/controllers/matrix/matrix_event_controller.rs @@ -11,7 +11,9 @@ use matrix_sdk::deserialized_responses::{TimelineEvent, TimelineEventKind}; use matrix_sdk::media::MediaEventContent; use matrix_sdk::room::MessagesOptions; use matrix_sdk::room::edit::EditedContent; +use matrix_sdk::ruma::api::client::receipt::create_receipt::v3::ReceiptType; use matrix_sdk::ruma::events::reaction::ReactionEventContent; +use matrix_sdk::ruma::events::receipt::ReceiptThread; use matrix_sdk::ruma::events::relation::Annotation; use matrix_sdk::ruma::events::room::message::{ MessageType, RoomMessageEvent, RoomMessageEventContent, RoomMessageEventContentWithoutRelation, @@ -266,3 +268,23 @@ pub async fn redact_event( } }) } + +/// Send receipt for event +pub async fn receipt( + client: MatrixClientExtractor, + path: web::Path, + event_path: web::Path, +) -> HttpResult { + let Some(room) = client.client.client.get_room(&path.room_id) else { + return Ok(HttpResponse::NotFound().json("Room not found")); + }; + + room.send_single_receipt( + ReceiptType::Read, + ReceiptThread::default(), + event_path.event_id.clone(), + ) + .await?; + + Ok(HttpResponse::Accepted().finish()) +} diff --git a/matrixgw_backend/src/main.rs b/matrixgw_backend/src/main.rs index 7623f65..1713864 100644 --- a/matrixgw_backend/src/main.rs +++ b/matrixgw_backend/src/main.rs @@ -188,6 +188,10 @@ async fn main() -> std::io::Result<()> { .route( "/api/matrix/room/{room_id}/event/{event_id}", web::delete().to(matrix_event_controller::redact_event), + ) + .route( + "/api/matrix/room/{room_id}/event/{event_id}/receipt", + web::post().to(matrix_event_controller::receipt), ) // Matrix media controller .route( diff --git a/matrixgw_frontend/src/api/matrix/MatrixApiEvent.ts b/matrixgw_frontend/src/api/matrix/MatrixApiEvent.ts index f3d81f9..497dc0d 100644 --- a/matrixgw_frontend/src/api/matrix/MatrixApiEvent.ts +++ b/matrixgw_frontend/src/api/matrix/MatrixApiEvent.ts @@ -140,4 +140,14 @@ export class MatrixApiEvent { uri: `/matrix/room/${room.id}/event/${event_id}`, }); } + + /** + * Send event receipt + */ + static async SendReceipt(room: Room, event_id: string): Promise { + await APIClient.exec({ + method: "POST", + uri: `/matrix/room/${room.id}/event/${event_id}/receipt`, + }); + } } diff --git a/matrixgw_frontend/src/widgets/messages/RoomWidget.tsx b/matrixgw_frontend/src/widgets/messages/RoomWidget.tsx index 77d52f1..61d33be 100644 --- a/matrixgw_frontend/src/widgets/messages/RoomWidget.tsx +++ b/matrixgw_frontend/src/widgets/messages/RoomWidget.tsx @@ -4,14 +4,37 @@ import type { Room } from "../../api/matrix/MatrixApiRoom"; import { RoomEventsManager } from "../../utils/RoomEventsManager"; import { RoomMessagesList } from "./RoomMessagesList"; import { SendMessageForm } from "./SendMessageForm"; +import { MatrixApiEvent } from "../../api/matrix/MatrixApiEvent"; +import { useSnackbar } from "../../hooks/contexts_provider/SnackbarProvider"; export function RoomWidget(p: { room: Room; users: UsersMap; manager: RoomEventsManager; }): React.ReactElement { + const snackbar = useSnackbar(); + + const receiptId = React.useRef(undefined); + + const handleRoomClick = async () => { + if (p.manager.messages.length === 0) return; + const latest = p.manager.messages[p.manager.messages.length - 1]; + if (latest.event_id === receiptId.current) return; + + receiptId.current = latest.event_id; + try { + await MatrixApiEvent.SendReceipt(p.room, latest.event_id); + } catch (e) { + console.error("Failed to send read receipt!", e); + snackbar(`Failed to send read receipt! ${e}`); + } + }; + return ( -
+