diff --git a/src/async_client.rs b/src/async_client.rs index f74e469e..00bb7e9b 100644 --- a/src/async_client.rs +++ b/src/async_client.rs @@ -14,11 +14,12 @@ // limitations under the License. use futures::future::{BoxFuture, Future, FutureExt}; +use std::collections::HashMap; use std::convert::{TryFrom, TryInto}; use std::sync::atomic::{AtomicU64, Ordering}; use std::sync::{Arc, Mutex, RwLock as SyncLock}; use std::time::{Duration, Instant}; -use tokio::sync::RwLock; +use tokio::sync::{RwLock, RwLockReadGuard}; use async_std::task::sleep; @@ -261,6 +262,22 @@ impl AsyncClient { &self.homeserver } + #[doc(hidden)] + /// Access to the underlying `BaseClient`. Used for testing and debugging so far. + pub async fn base_client(&self) -> RwLockReadGuard<'_, BaseClient> { + self.base_client.read().await + } + + /// Calculate the room name from a `RoomId`, returning a string. + pub async fn get_room_name(&self, room_id: &str) -> Option { + self.base_client.read().await.calculate_room_name(room_id) + } + + /// Calculate the room names this client knows about. + pub async fn get_room_names(&self) -> Vec { + self.base_client.read().await.calculate_room_names() + } + /// Add a callback that will be called every time the client receives a room /// event /// diff --git a/src/base_client.rs b/src/base_client.rs index a22c338b..0bb7c4fc 100644 --- a/src/base_client.rs +++ b/src/base_client.rs @@ -24,7 +24,7 @@ use crate::events::room::{ name::{NameEvent}, }; use crate::events::EventResult; -use crate::identifiers::{RoomAliasId}; +use crate::identifiers::RoomAliasId; use crate::session::Session; use std::sync::{Arc, RwLock}; @@ -99,7 +99,7 @@ impl RoomName { true } - pub fn calculate_name(&self, room_id: &RoomId, members: &HashMap) -> String { + pub fn calculate_name(&self, room_id: &str, members: &HashMap) -> String { // https://github.com/matrix-org/matrix-js-sdk/blob/33941eb37bffe41958ba9887fc8070dfb1a0ee76/src/models/room.js#L1823 // the order in which we check for a name ^^ if let Some(name) = &self.name { @@ -113,9 +113,10 @@ impl RoomName { let mut names = members.values().flat_map(|m| m.display_name.clone()).take(3).collect::>(); if names.is_empty() { + // TODO implement the rest of matrix-js-sdk handling of room names format!("Room {}", room_id) } else { - // stablize order + // stabilize order names.sort(); names.join(", ").to_string() } @@ -351,6 +352,17 @@ impl Client { } } + pub(crate) fn calculate_room_name(&self, room_id: &str) -> Option { + self.joined_rooms.get(room_id) + .and_then(|r| r.read().map(|r| r.room_name.calculate_name(room_id, &r.members)).ok()) + } + + pub(crate) fn calculate_room_names(&self) -> Vec { + self.joined_rooms.iter() + .flat_map(|(id, room)| room.read().map(|r| r.room_name.calculate_name(id, &r.members)).ok()) + .collect() + } + fn get_or_create_room(&mut self, room_id: &str) -> &mut Arc> { #[allow(clippy::or_fun_call)] self.joined_rooms diff --git a/tests/async_client_tests.rs b/tests/async_client_tests.rs index 3c1e1251..23c215bb 100644 --- a/tests/async_client_tests.rs +++ b/tests/async_client_tests.rs @@ -58,3 +58,36 @@ fn sync() { assert!(rt.block_on(client.sync_token()).is_some()); } + + +#[test] +fn timeline() { + let mut rt = Runtime::new().unwrap(); + + let homeserver = Url::from_str(&mockito::server_url()).unwrap(); + + let session = Session { + access_token: "1234".to_owned(), + user_id: UserId::try_from("@example:example.com").unwrap(), + device_id: "DEVICEID".to_owned(), + }; + + let _m = mock( + "GET", + Matcher::Regex(r"^/_matrix/client/r0/sync\?.*$".to_string()), + ) + .with_status(200) + .with_body_from_file("tests/data/timeline.json") + .create(); + + let mut client = AsyncClient::new(homeserver, Some(session)).unwrap(); + + let sync_settings = SyncSettings::new().timeout(3000).unwrap(); + + let _response = rt.block_on(client.sync(sync_settings)).unwrap(); + + assert_eq!(vec!["tutorial"], rt.block_on(client.get_room_names())); + assert_eq!(Some("tutorial".into()), rt.block_on(client.get_room_name("!SVkFJHzfwvuaIEawgC:localhost"))); + + // rt.block_on(async { println!("{:#?}", &client.base_client().await.joined_rooms ) }); +} diff --git a/tests/data/timeline.json b/tests/data/timeline.json new file mode 100644 index 00000000..3b898e20 --- /dev/null +++ b/tests/data/timeline.json @@ -0,0 +1,226 @@ +{ + "device_one_time_keys_count": {}, + "next_batch": "s526_47314_0_7_1_1_1_11444_1", + "device_lists": { + "changed": [ + "@example:example.org" + ], + "left": [] + }, + + "rooms": { + "invite": {}, + "join": { + "!SVkFJHzfwvuaIEawgC:localhost": { + "account_data": { + "events": [] + }, + "ephemeral": { + "events": [ + { + "content": { + "$151680659217152dPKjd:localhost": { + "m.read": { + "@example:localhost": { + "ts": 1516809890615 + } + } + } + }, + "type": "m.receipt" + } + ] + }, + "state": { + "events": [ + { + "content": { + "join_rule": "public" + }, + "event_id": "$15139375514WsgmR:localhost", + "origin_server_ts": 1513937551539, + "sender": "@example:localhost", + "state_key": "", + "type": "m.room.join_rules", + "unsigned": { + "age": 7034220355 + } + }, + { + "content": { + "avatar_url": null, + "displayname": "example", + "membership": "join" + }, + "event_id": "$151800140517rfvjc:localhost", + "membership": "join", + "origin_server_ts": 1518001405556, + "sender": "@example:localhost", + "state_key": "@example:localhost", + "type": "m.room.member", + "unsigned": { + "age": 2970366338, + "replaces_state": "$151800111315tsynI:localhost" + } + }, + { + "content": { + "history_visibility": "shared" + }, + "event_id": "$15139375515VaJEY:localhost", + "origin_server_ts": 1513937551613, + "sender": "@example:localhost", + "state_key": "", + "type": "m.room.history_visibility", + "unsigned": { + "age": 7034220281 + } + }, + { + "content": { + "creator": "@example:localhost" + }, + "event_id": "$15139375510KUZHi:localhost", + "origin_server_ts": 1513937551203, + "sender": "@example:localhost", + "state_key": "", + "type": "m.room.create", + "unsigned": { + "age": 7034220691 + } + }, + { + "content": { + "aliases": [ + "#tutorial:localhost" + ] + }, + "event_id": "$15139375516NUgtD:localhost", + "origin_server_ts": 1513937551720, + "sender": "@example:localhost", + "state_key": "localhost", + "type": "m.room.aliases", + "unsigned": { + "age": 7034220174 + } + }, + { + "content": { + "topic": "\ud83d\ude00" + }, + "event_id": "$151957878228ssqrJ:localhost", + "origin_server_ts": 1519578782185, + "sender": "@example:localhost", + "state_key": "", + "type": "m.room.topic", + "unsigned": { + "age": 1392989709, + "prev_content": { + "topic": "test" + }, + "prev_sender": "@example:localhost", + "replaces_state": "$151957069225EVYKm:localhost" + } + }, + { + "content": { + "ban": 50, + "events": { + "m.room.avatar": 50, + "m.room.canonical_alias": 50, + "m.room.history_visibility": 100, + "m.room.name": 50, + "m.room.power_levels": 100 + }, + "events_default": 0, + "invite": 0, + "kick": 50, + "redact": 50, + "state_default": 50, + "users": { + "@example:localhost": 100 + }, + "users_default": 0 + }, + "event_id": "$15139375512JaHAW:localhost", + "origin_server_ts": 1513937551359, + "sender": "@example:localhost", + "state_key": "", + "type": "m.room.power_levels", + "unsigned": { + "age": 7034220535 + } + }, + { + "content": { + "alias": "#tutorial:localhost" + }, + "event_id": "$15139375513VdeRF:localhost", + "origin_server_ts": 1513937551461, + "sender": "@example:localhost", + "state_key": "", + "type": "m.room.canonical_alias", + "unsigned": { + "age": 7034220433 + } + }, + { + "content": { + "avatar_url": null, + "displayname": "example2", + "membership": "join" + }, + "event_id": "$152034824468gOeNB:localhost", + "membership": "join", + "origin_server_ts": 1520348244605, + "sender": "@example2:localhost", + "state_key": "@example2:localhost", + "type": "m.room.member", + "unsigned": { + "age": 623527289, + "prev_content": { + "membership": "leave" + }, + "prev_sender": "@example:localhost", + "replaces_state": "$152034819067QWJxM:localhost" + } + } + ] + }, + "timeline": { + "events": [ + { + "content": { + "body": "baba", + "format": "org.matrix.custom.html", + "formatted_body": "baba", + "msgtype": "m.text" + }, + "event_id": "$152037280074GZeOm:localhost", + "origin_server_ts": 1520372800469, + "sender": "@example:localhost", + "type": "m.room.message", + "unsigned": { + "age": 598971425 + } + } + ], + "limited": true, + "prev_batch": "t392-516_47314_0_7_1_1_1_11444_1" + }, + "unread_notifications": { + "highlight_count": 0, + "notification_count": 11 + } + } + }, + "leave": {} + }, + "to_device": { + "events": [] + }, + + "presence": { + "events": [] + } +}