Use room display name instead of alias/id

This commit is contained in:
2023-11-06 22:32:57 +01:00
parent 55e814e955
commit b9055dc5d3

View File

@ -20,12 +20,13 @@ use std::sync::{Arc, OnceLock};
use chrono::{offset::Local, DateTime}; use chrono::{offset::Local, DateTime};
use color_eyre::eyre::{eyre, Result}; use color_eyre::eyre::{eyre, Result};
use eyeball_im::VectorDiff; use eyeball_im::VectorDiff;
use futures::future::OptionFuture;
use futures::stream::FuturesUnordered;
use futures::{FutureExt, Stream, StreamExt}; use futures::{FutureExt, Stream, StreamExt};
use itertools::Itertools; use itertools::Itertools;
use matrix_sdk::async_trait; use matrix_sdk::async_trait;
use matrix_sdk::ruma::{OwnedRoomId, RoomId}; use matrix_sdk::ruma::{OwnedRoomId, RoomId};
use matrix_sdk::Client; use matrix_sdk::{Client, DisplayName, Room, RoomInfo};
use matrix_sdk::Room;
use matrix_sdk_ui::timeline::{ use matrix_sdk_ui::timeline::{
BackPaginationStatus, PaginationOptions, RoomExt, Timeline, TimelineItem, TimelineItemKind, BackPaginationStatus, PaginationOptions, RoomExt, Timeline, TimelineItem, TimelineItemKind,
VirtualTimelineItem, VirtualTimelineItem,
@ -80,11 +81,14 @@ macro_rules! text {
pub struct SingleClientRoomBuffer { pub struct SingleClientRoomBuffer {
room_id: OwnedRoomId, room_id: OwnedRoomId,
client: Client, client: Client,
items: imbl::vector::Vector<(OwnedBufferItemContent, Prerender)>, items: imbl::vector::Vector<(OwnedBufferItemContent, Prerender)>,
// TODO: get rid of this trait object, we know it's matrix_sdk_ui::timeline::TimelineStream // TODO: get rid of this trait object, we know it's matrix_sdk_ui::timeline::TimelineStream
stream: Box<dyn Stream<Item = Vec<VectorDiff<Arc<TimelineItem>>>> + Send + Sync + Unpin>, timeline_stream: Box<dyn Stream<Item = Vec<VectorDiff<Arc<TimelineItem>>>> + Send + Sync + Unpin>,
timeline: Arc<Timeline>, timeline: Arc<Timeline>,
back_pagination_request: AtomicU16, back_pagination_request: AtomicU16,
roominfo_subscriber: eyeball::Subscriber<RoomInfo>,
} }
impl DynamicUsage for SingleClientRoomBuffer { impl DynamicUsage for SingleClientRoomBuffer {
@ -111,18 +115,27 @@ impl DynamicUsage for SingleClientRoomBuffer {
} }
impl SingleClientRoomBuffer { impl SingleClientRoomBuffer {
async fn poll_updates(&mut self) { /// If the room info was updated, returns it
async fn poll_updates(&mut self) -> Option<RoomInfo> {
let back_pagination_request = self.back_pagination_request.swap(0, Ordering::Relaxed); let back_pagination_request = self.back_pagination_request.swap(0, Ordering::Relaxed);
if back_pagination_request > 0 { if back_pagination_request > 0 {
// TODO: run this concurrently with stream.next() below // TODO: run this concurrently with timeline_stream.next() below
self.spawn_back_pagination(back_pagination_request).await; self.spawn_back_pagination(back_pagination_request).await;
} }
if let Some(changes) = self.stream.next().await { tokio::select! {
biased;
roominfo = self.roominfo_subscriber.next() => {
Some(roominfo.expect("reached end of roominfo_subscriber"))
},
changes = self.timeline_stream.next() => {
let Some(changes) = changes else { return None; };
for change in changes { for change in changes {
change change
.map(|item| (self.format_timeline_item(item), Prerender::new())) .map(|item| (self.format_timeline_item(item), Prerender::new()))
.apply(&mut self.items); .apply(&mut self.items);
} }
None
}
} }
} }
@ -315,6 +328,9 @@ impl SingleClientRoomBuffer {
pub struct RoomBuffer { pub struct RoomBuffer {
room_id: OwnedRoomId, room_id: OwnedRoomId,
initialized_roominfo: bool,
display_name: Option<DisplayName>,
// It's unlikely users will join the same room with more than one account; // It's unlikely users will join the same room with more than one account;
// avoid a useless heap allocation for the usual case. // avoid a useless heap allocation for the usual case.
buffers: SmallVec<[SingleClientRoomBuffer; 1]>, buffers: SmallVec<[SingleClientRoomBuffer; 1]>,
@ -341,17 +357,17 @@ impl DynamicUsage for RoomBuffer {
impl RoomBuffer { impl RoomBuffer {
pub async fn new(initial_client: Client, room_id: OwnedRoomId) -> Result<Self> { pub async fn new(initial_client: Client, room_id: OwnedRoomId) -> Result<Self> {
let mut self_ = RoomBuffer { let mut self_ = RoomBuffer {
buffers: SmallVec::new(),
room_id, room_id,
initialized_roominfo: false,
display_name: None,
buffers: SmallVec::new(),
}; };
self_.add_client(initial_client).await?; self_.add_client(initial_client.clone()).await?;
Ok(self_) Ok(self_)
} }
pub async fn add_client(&mut self, client: Client) -> Result<()> { pub async fn add_client(&mut self, client: Client) -> Result<()> {
let timeline = client let room = client.get_room(&self.room_id).ok_or_else(|| {
.get_room(&self.room_id)
.ok_or_else(|| {
tracing::error!( tracing::error!(
"Adding {:?} for {:?}, but it does not know this room ({} other clients know this room)", "Adding {:?} for {:?}, but it does not know this room ({} other clients know this room)",
client, client,
@ -359,11 +375,9 @@ impl RoomBuffer {
self.buffers.len() self.buffers.len()
); );
eyre!("Unknown room {} for client {:?}", self.room_id, client) eyre!("Unknown room {} for client {:?}", self.room_id, client)
})? })?;
.timeline_builder() let timeline = room.timeline_builder().build().await;
.build() let (items, timeline_stream) = timeline.subscribe_batched().await;
.await;
let (items, stream) = timeline.subscribe_batched().await;
tracing::info!( tracing::info!(
"Added client for {}, initial items: {:?}", "Added client for {}, initial items: {:?}",
self.room_id, self.room_id,
@ -377,8 +391,9 @@ impl RoomBuffer {
.into_iter() .into_iter()
.map(|item| (text!("Initial item: {:#?}", item), Prerender::new())) .map(|item| (text!("Initial item: {:#?}", item), Prerender::new()))
.collect(), .collect(),
stream: Box::new(stream), timeline_stream: Box::new(timeline_stream),
back_pagination_request: AtomicU16::new(0), back_pagination_request: AtomicU16::new(0),
roominfo_subscriber: room.subscribe_info(),
}); });
Ok(()) Ok(())
} }
@ -387,14 +402,21 @@ impl RoomBuffer {
#[async_trait] #[async_trait]
impl Buffer for RoomBuffer { impl Buffer for RoomBuffer {
fn short_name(&self) -> String { fn short_name(&self) -> String {
self
.display_name
.as_ref()
.map(|dn| dn.to_string())
.unwrap_or_else(|| {
self self
.buffers .buffers
.iter() .iter()
.flat_map(|buf| buf.client.get_room(&self.room_id)) .flat_map(|buf| buf.client.get_room(&self.room_id))
.flat_map(|room| room.canonical_alias()) // TODO: .display_name() is better, but async :( .flat_map(|room| room.canonical_alias())
.map(|alias| alias.as_str().to_owned()) .map(|alias| alias.as_str().to_owned())
.next() .next()
.unwrap_or(self.room_id.as_str().to_owned()) .unwrap_or(self.room_id.as_str().to_owned())
.clone()
})
} }
fn room_id(&self) -> Option<&RoomId> { fn room_id(&self) -> Option<&RoomId> {
@ -402,13 +424,86 @@ impl Buffer for RoomBuffer {
} }
async fn poll_updates(&mut self) { async fn poll_updates(&mut self) {
futures::future::join_all( let room = if self.initialized_roominfo {
None
} else {
Some(
self self
.buffers .buffers
.iter_mut() .first()
.map(SingleClientRoomBuffer::poll_updates), .unwrap_or_else(|| panic!("No sub-buffer for {}", self.room_id))
.client
.get_room(&self.room_id)
.unwrap_or_else(|| panic!("Room {} disappeared", self.room_id)),
) )
.await; };
let mut roominfo_update = self
.buffers
.iter_mut()
.map(|buf| async {
let roominfo = buf.poll_updates().await;
(buf, roominfo)
})
.collect::<FuturesUnordered<_>>();
let res = if self.initialized_roominfo {
roominfo_update.next().await
} else {
let room = room.unwrap(); // Set above iff !initialized_roominfo
// Poll both roominfo_update and the display name, so we start getting new messages
// early if the user is in a hurry
tokio::select! {
biased;
dn = room.display_name() => {
match dn {
Ok(dn) => {
tracing::debug!("Initialized display name for {}: {}", self.room_id, dn);
self.display_name = Some(dn);
},
Err(e) => {
tracing::error!(
"Error while resolving initial display name for {}: {}",
self.room_id,
e
);
},
};
self.initialized_roominfo = true;
None
}
res = roominfo_update.next() => { res }
}
};
let Some((buf, roominfo)) = res else {
return;
};
let Some(roominfo) = roominfo else {
return;
};
let Some(room) = buf.client.get_room(&self.room_id) else {
return;
};
// This blocks any other update to the room while matrix-sdk computes the display
// name. Let's pretend it's a feature. (Although it's probably pretty bad when
// joined to the room with multiple clients and they all get the same update and
// have to resolve the name one by one...)
self.display_name = match room.display_name().await {
Ok(dn) => {
tracing::debug!("Setting display name for {}: {}", self.room_id, dn);
Some(dn)
},
Err(e) => {
tracing::error!(
"Error while resolving display name for {}: {}",
self.room_id,
e
);
None
},
};
self.initialized_roominfo = true;
} }
fn content<'a>(&'a self) -> Box<dyn Iterator<Item = BufferItem<'a>> + 'a> { fn content<'a>(&'a self) -> Box<dyn Iterator<Item = BufferItem<'a>> + 'a> {