From 2f2cebe84d319608631273cef32c8236868e5baa Mon Sep 17 00:00:00 2001
From: Jason Volk <jason@zemos.net>
Date: Tue, 12 Nov 2024 03:46:31 +0000
Subject: [PATCH] implement local room preview

Signed-off-by: Jason Volk <jason@zemos.net>
---
 src/api/client/room/initial_sync.rs | 72 +++++++++++++++++++++++++++++
 src/api/client/room/mod.rs          |  3 +-
 src/api/router.rs                   |  7 +--
 3 files changed, 75 insertions(+), 7 deletions(-)
 create mode 100644 src/api/client/room/initial_sync.rs

diff --git a/src/api/client/room/initial_sync.rs b/src/api/client/room/initial_sync.rs
new file mode 100644
index 00000000..16b3a53b
--- /dev/null
+++ b/src/api/client/room/initial_sync.rs
@@ -0,0 +1,72 @@
+use axum::extract::State;
+use conduit::{at, utils::BoolExt, Err, Result};
+use futures::StreamExt;
+use ruma::api::client::room::initial_sync::v3::{PaginationChunk, Request, Response};
+
+use crate::Ruma;
+
+const LIMIT_MAX: usize = 100;
+
+pub(crate) async fn room_initial_sync_route(
+	State(services): State<crate::State>, body: Ruma<Request>,
+) -> Result<Response> {
+	let room_id = &body.room_id;
+
+	if !services
+		.rooms
+		.state_accessor
+		.user_can_see_state_events(body.sender_user(), room_id)
+		.await
+	{
+		return Err!(Request(Forbidden("No room preview available.")));
+	}
+
+	let limit = LIMIT_MAX;
+	let events: Vec<_> = services
+		.rooms
+		.timeline
+		.pdus_rev(None, room_id, None)
+		.await?
+		.take(limit)
+		.collect()
+		.await;
+
+	let state: Vec<_> = services
+		.rooms
+		.state_accessor
+		.room_state_full_pdus(room_id)
+		.await?
+		.into_iter()
+		.map(|pdu| pdu.to_state_event())
+		.collect();
+
+	let messages = PaginationChunk {
+		start: events.last().map(at!(0)).as_ref().map(ToString::to_string),
+
+		end: events
+			.first()
+			.map(at!(0))
+			.as_ref()
+			.map(ToString::to_string)
+			.unwrap_or_default(),
+
+		chunk: events
+			.into_iter()
+			.map(at!(1))
+			.map(|pdu| pdu.to_room_event())
+			.collect(),
+	};
+
+	Ok(Response {
+		room_id: room_id.to_owned(),
+		account_data: None,
+		state: state.into(),
+		messages: messages.chunk.is_empty().or_some(messages),
+		visibility: services.rooms.directory.visibility(room_id).await.into(),
+		membership: services
+			.rooms
+			.state_cache
+			.user_membership(body.sender_user(), room_id)
+			.await,
+	})
+}
diff --git a/src/api/client/room/mod.rs b/src/api/client/room/mod.rs
index fa2d168f..16fcadab 100644
--- a/src/api/client/room/mod.rs
+++ b/src/api/client/room/mod.rs
@@ -1,9 +1,10 @@
 mod aliases;
 mod create;
 mod event;
+mod initial_sync;
 mod upgrade;
 
 pub(crate) use self::{
 	aliases::get_room_aliases_route, create::create_room_route, event::get_room_event_route,
-	upgrade::upgrade_room_route,
+	initial_sync::room_initial_sync_route, upgrade::upgrade_room_route,
 };
diff --git a/src/api/router.rs b/src/api/router.rs
index 1df4342f..4bdd692d 100644
--- a/src/api/router.rs
+++ b/src/api/router.rs
@@ -183,8 +183,7 @@ pub fn build(router: Router<State>, server: &Server) -> Router<State> {
 		.ruma_route(&client::well_known_support)
 		.ruma_route(&client::well_known_client)
 		.route("/_conduwuit/server_version", get(client::conduwuit_server_version))
-		.route("/_matrix/client/r0/rooms/:room_id/initialSync", get(initial_sync))
-		.route("/_matrix/client/v3/rooms/:room_id/initialSync", get(initial_sync))
+		.ruma_route(&client::room_initial_sync_route)
 		.route("/client/server.json", get(client::syncv3_client_server_json));
 
 	if config.allow_federation {
@@ -285,10 +284,6 @@ async fn redirect_legacy_preview(uri: Uri) -> impl IntoResponse {
 	Redirect::temporary(&uri)
 }
 
-async fn initial_sync(_uri: Uri) -> impl IntoResponse {
-	err!(Request(GuestAccessForbidden("Guest access not implemented")))
-}
-
 async fn legacy_media_disabled() -> impl IntoResponse { err!(Request(Forbidden("Unauthenticated media is disabled."))) }
 
 async fn federation_disabled() -> impl IntoResponse { err!(Request(Forbidden("Federation is disabled."))) }