3 Commits

Author SHA1 Message Date
19d64d7ac5 ♻️ Render Room avatar using the RoomMember ones, if not set 2024-05-22 16:44:07 +02:00
35e191eb62 🗑️ WorkerTask::GetRoomMembers isn't used, remove it 2024-05-22 16:21:13 +02:00
8c244ce4a7 ⬆️ Use dioxus main branch for now 2024-05-22 15:32:25 +02:00
8 changed files with 137 additions and 164 deletions

View File

@@ -43,18 +43,19 @@ tracing-forest = "0.1.6"
turf = "0.8.0" turf = "0.8.0"
# Dioxus # Dioxus
# dioxus-free-icons = { version = "0.8.*", features = ["ionicons", "font-awesome-solid"] } dioxus = { version = "0.5", default-features = false }
dioxus-free-icons = { path = "../dioxuslabs/dioxus-free-icons/packages/lib", features = ["ionicons", "font-awesome-solid"] } dioxus-free-icons = { version = "0.8", features = ["ionicons", "font-awesome-solid"] }
modx = "0.1.2" modx = "0.1.2"
[patch.crates-io]
dioxus = { git = "https://github.com/DioxusLabs/dioxus.git" }
[target.'cfg(target_family = "wasm")'.dependencies] [target.'cfg(target_family = "wasm")'.dependencies]
# Logging/tracing # Logging/tracing
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
tracing-web = "0.1.3" tracing-web = "0.1.3"
# Dioxus # Dioxus
# dioxus = { version = "0.5", features = ["web"] } dioxus = { features = ["web"] }
dioxus = { path = "../dioxuslabs/dioxus/packages/dioxus", features = ["web"] }
web-sys = "0.3.69" web-sys = "0.3.69"
# Matrix # Matrix
matrix-sdk = { git = "https://github.com/matrix-org/matrix-rust-sdk.git", default-features = false, features = ["rustls-tls", "js"] } matrix-sdk = { git = "https://github.com/matrix-org/matrix-rust-sdk.git", default-features = false, features = ["rustls-tls", "js"] }
@@ -65,8 +66,7 @@ time = "0.3.36"
# Logging/tracing # Logging/tracing
tracing-subscriber = { version = "0.3.18", features = ["env-filter", "time"] } tracing-subscriber = { version = "0.3.18", features = ["env-filter", "time"] }
# Dioxus # Dioxus
# dioxus = { version = "0.5", features = ["desktop"] } dioxus = { features = ["desktop"] }
dioxus = { path = "../dioxuslabs/dioxus/packages/dioxus", features = ["desktop"] }
# Matrix # Matrix
matrix-sdk = { git = "https://github.com/matrix-org/matrix-rust-sdk.git", default-features = false, features = ["rustls-tls"] } matrix-sdk = { git = "https://github.com/matrix-org/matrix-rust-sdk.git", default-features = false, features = ["rustls-tls"] }

View File

@@ -40,7 +40,6 @@ pub async fn login(mut rx: UnboundedReceiver<bool>, session: &GlobalSignal<Sessi
let requester = Rc::new(requester); let requester = Rc::new(requester);
dioxus::prelude::spawn(async move { dioxus::prelude::spawn(async move {
// ACCOUNT.write().set_messaging_provider(requester.clone());
ACCOUNT.write().set_messaging_provider(requester.clone()); ACCOUNT.write().set_messaging_provider(requester.clone());
let _ = requester let _ = requester

View File

@@ -44,7 +44,6 @@ pub trait RoomMessagingConsumerInterface {
#[async_trait(?Send)] #[async_trait(?Send)]
pub trait RoomMessagingProviderInterface { pub trait RoomMessagingProviderInterface {
async fn get_avatar(&self, id: &RoomId) -> anyhow::Result<Option<Avatar>>; async fn get_avatar(&self, id: &RoomId) -> anyhow::Result<Option<Avatar>>;
async fn get_members(&self, id: &RoomId) -> anyhow::Result<Vec<RoomMember>>;
} }
#[async_trait(?Send)] #[async_trait(?Send)]
@@ -62,8 +61,8 @@ pub trait SpaceMessagingProviderInterface {}
pub trait MemberMessagingProviderInterface { pub trait MemberMessagingProviderInterface {
async fn get_avatar( async fn get_avatar(
&self, &self,
room_id: &RoomId, avatar_url: Option<AvatarUrl>,
user_id: &UserId, room_id: RoomId,
avatar_url: &Option<AvatarUrl>, user_id: UserId,
) -> anyhow::Result<Option<Avatar>>; ) -> anyhow::Result<Option<Avatar>>;
} }

View File

@@ -7,8 +7,7 @@ use std::{
use async_trait::async_trait; use async_trait::async_trait;
use futures::future::{join, join_all}; use futures::future::{join, join_all};
use matrix_sdk::ruma::OwnedRoomId; use matrix_sdk::{ruma::OwnedRoomId, RoomState as MatrixRoomState};
use matrix_sdk::RoomState as MatrixRoomState;
use tracing::{debug, debug_span, error, instrument, trace}; use tracing::{debug, debug_span, error, instrument, trace};
use super::{ use super::{
@@ -80,11 +79,12 @@ impl PartialEq for Room {
impl Room { impl Room {
pub fn new( pub fn new(
id: RoomId, id: RoomId,
spaces: Vec<SpaceId>, // TODO: move space at the end of the list of params
name: Option<String>, name: Option<String>,
topic: Option<String>, topic: Option<String>,
is_direct: Option<bool>, is_direct: Option<bool>,
state: Option<MatrixRoomState>, state: Option<MatrixRoomState>,
spaces: Vec<SpaceId>,
) -> Self { ) -> Self {
Self { Self {
id, id,
@@ -94,10 +94,8 @@ impl Room {
is_direct, is_direct,
state, state,
avatar: RefCell::new(None), avatar: RefCell::new(None),
invitations: RefCell::new(HashMap::new()), invitations: RefCell::new(HashMap::new()),
members: RefCell::new(HashMap::new()), members: RefCell::new(HashMap::new()),
spaces, spaces,
messaging_provider: None, messaging_provider: None,
@@ -134,11 +132,6 @@ impl Room {
self.topic = topic; self.topic = topic;
} }
#[allow(dead_code)]
pub fn is_direct(&self) -> &Option<bool> {
&self.is_direct
}
#[allow(dead_code)] #[allow(dead_code)]
pub fn state(&self) -> &Option<MatrixRoomState> { pub fn state(&self) -> &Option<MatrixRoomState> {
&self.state &self.state
@@ -161,6 +154,29 @@ impl Room {
store.on_invitation(invitation); store.on_invitation(invitation);
} }
} }
#[instrument(name = "Room", skip_all)]
fn add_member(&self, member: RoomMember) {
let mut members = self.members.borrow_mut();
members.insert(member.id().clone(), member.clone());
// USe the member display name to name the room if it's direct and has no name set.
if self.name.borrow().is_none() && members.len() == 1 {
if let Some(member_display_name) = member.display_name() {
let name = Some(member_display_name.clone());
self.name.borrow_mut().clone_from(&name);
if let Some(store) = self.store.borrow().as_ref() {
store.on_new_name(name);
}
}
}
if let Some(store) = self.store.borrow().as_ref() {
store.on_new_member(member);
}
} }
pub async fn get_avatar(&self) -> Option<Avatar> { pub async fn get_avatar(&self) -> Option<Avatar> {
@@ -172,7 +188,7 @@ impl Room {
return Some(avatar); return Some(avatar);
} else { } else {
debug!("The room has no avatar... let's generate one"); debug!("The room has no avatar... let's generate one");
match self.render_room_avatar_with_members().await { match self.gen_room_avatar_with_members().await {
Ok(avatar) => { Ok(avatar) => {
if let Some(avatar) = avatar { if let Some(avatar) = avatar {
return Some(avatar); return Some(avatar);
@@ -191,50 +207,42 @@ impl Room {
self.avatar.borrow().clone() self.avatar.borrow().clone()
} }
async fn render_room_avatar_with_members(&self) -> anyhow::Result<Option<Avatar>> { #[instrument(name = "Room", skip_all)]
if let Some(requester) = &self.messaging_provider { async fn gen_room_avatar_with_members(&self) -> anyhow::Result<Option<Avatar>> {
match requester.get_members(&self.id).await { let mut account_member = None::<&RoomMember>;
Ok(members) => { let mut other_members = Vec::<&RoomMember>::new();
let mut account_member = None::<&RoomMember>;
let mut other_members = Vec::<&RoomMember>::new();
for member in &members { let members = self.members.borrow();
if member.is_account_user() { for member in members.values() {
account_member = Some(member); if member.is_account_user() {
} else { account_member = Some(member);
other_members.push(member); } else {
} other_members.push(member);
}
let other_avatars_futures =
join_all(other_members.iter().map(|member| member.get_avatar()));
let (other_avatars, account_avatar) = if let Some(account_member) =
account_member
{
join(other_avatars_futures, account_member.get_avatar()).await
} else {
(
join_all(other_members.iter().map(|member| member.get_avatar())).await,
None,
)
};
let other_avatars: Vec<Vec<u8>> = other_avatars.into_iter().flatten().collect();
return Ok(Some(create_mozaik(
256,
256,
&other_avatars,
&account_avatar,
)));
}
Err(err) => {
error!("err={}", err);
}
} }
} }
Ok(None)
let other_avatars_futures =
join_all(other_members.iter().map(|member| member.get_avatar()));
let (other_avatars, account_avatar) = if let Some(account_member) = account_member {
join(other_avatars_futures, account_member.get_avatar()).await
} else {
(
join_all(other_members.iter().map(|member| member.get_avatar())).await,
None,
)
};
let other_avatars: Vec<Vec<u8>> = other_avatars.into_iter().flatten().collect();
if account_avatar.is_some() || !other_avatars.is_empty() {
let _guard = debug_span!("AvatarRendering").entered();
Ok(Some(
create_mozaik(256, 256, other_avatars, account_avatar).await,
))
} else {
Ok(None)
}
} }
} }
@@ -278,6 +286,10 @@ impl RoomMessagingConsumerInterface for Room {
#[instrument(name = "Room", skip_all)] #[instrument(name = "Room", skip_all)]
async fn on_new_avatar(&self, avatar: Option<Avatar>) { async fn on_new_avatar(&self, avatar: Option<Avatar>) {
trace!("on_new_avatar"); trace!("on_new_avatar");
self.avatar.borrow_mut().clone_from(&avatar);
if let Some(store) = self.store.borrow().as_ref() {
store.on_new_avatar(avatar);
}
} }
} }
@@ -295,6 +307,10 @@ impl RoomStoreConsumerInterface for Room {
self.name.borrow().clone() self.name.borrow().clone()
} }
async fn avatar(&self) -> Option<Avatar> {
self.get_avatar().await
}
fn spaces(&self) -> &Vec<SpaceId> { fn spaces(&self) -> &Vec<SpaceId> {
&self.spaces &self.spaces
} }

View File

@@ -4,7 +4,7 @@ use std::{
rc::Rc, rc::Rc,
}; };
use matrix_sdk::{room::RoomMember as MatrixRoomMember, ruma::OwnedRoomId}; use matrix_sdk::ruma::OwnedMxcUri;
use tracing::error; use tracing::error;
use super::{ use super::{
@@ -13,9 +13,14 @@ use super::{
room::RoomId, room::RoomId,
}; };
pub type AvatarUrl = OwnedMxcUri;
#[derive(Clone)] #[derive(Clone)]
pub struct RoomMember { pub struct RoomMember {
id: UserId, id: UserId,
display_name: Option<String>,
avatar_url: Option<AvatarUrl>,
room_id: RoomId, room_id: RoomId,
is_account_user: bool, is_account_user: bool,
@@ -26,14 +31,18 @@ pub struct RoomMember {
} }
impl RoomMember { impl RoomMember {
fn new( pub fn new(
id: UserId, id: UserId,
display_name: Option<String>,
avatar_url: Option<AvatarUrl>,
room_id: RoomId, room_id: RoomId,
is_account_user: bool, is_account_user: bool,
messaging_provider: Rc<dyn MemberMessagingProviderInterface>, messaging_provider: Rc<dyn MemberMessagingProviderInterface>,
) -> Self { ) -> Self {
Self { Self {
id, id,
display_name,
avatar_url,
room_id, room_id,
is_account_user, is_account_user,
avatar: RefCell::new(None), avatar: RefCell::new(None),
@@ -41,24 +50,14 @@ impl RoomMember {
} }
} }
// TODO: Use a factory instead...
pub async fn from_matrix(
matrix_room_member: &MatrixRoomMember,
room_id: &OwnedRoomId,
messaging_provider: Rc<dyn MemberMessagingProviderInterface>,
) -> Self {
Self::new(
UserId::from(matrix_room_member.user_id()),
room_id.clone(),
matrix_room_member.is_account_user(),
messaging_provider,
)
}
pub fn id(&self) -> &UserId { pub fn id(&self) -> &UserId {
&self.id &self.id
} }
pub fn display_name(&self) -> &Option<String> {
&self.display_name
}
#[allow(dead_code)] #[allow(dead_code)]
pub fn room_id(&self) -> &RoomId { pub fn room_id(&self) -> &RoomId {
&self.room_id &self.room_id
@@ -71,7 +70,11 @@ impl RoomMember {
pub async fn get_avatar(&self) -> Option<Avatar> { pub async fn get_avatar(&self) -> Option<Avatar> {
match self match self
.messaging_provider .messaging_provider
.get_avatar(&self.room_id, &self.id) .get_avatar(
self.avatar_url.clone(),
self.room_id.clone(),
self.id.clone(),
)
.await .await
{ {
Ok(avatar) => avatar, Ok(avatar) => avatar,

View File

@@ -10,7 +10,7 @@ use matrix_sdk::{
config::SyncSettings, config::SyncSettings,
event_handler::Ctx, event_handler::Ctx,
media::{MediaFormat, MediaRequest, MediaThumbnailSize}, media::{MediaFormat, MediaRequest, MediaThumbnailSize},
room::{ParentSpace, Room, RoomMember}, room::{ParentSpace, Room},
ruma::{ ruma::{
api::client::media::get_content_thumbnail::v3::Method, api::client::media::get_content_thumbnail::v3::Method,
events::{ events::{
@@ -26,7 +26,7 @@ use matrix_sdk::{
}, },
uint, OwnedMxcUri, OwnedRoomId, OwnedUserId, RoomId, UserId, uint, OwnedMxcUri, OwnedRoomId, OwnedUserId, RoomId, UserId,
}, },
Client as MatrixClient, RoomMemberships, RoomState, Client as MatrixClient, RoomState,
}; };
use tokio::sync::{ use tokio::sync::{
broadcast, broadcast,
@@ -702,21 +702,6 @@ impl Client {
} }
} }
async fn get_room_members(&mut self, room_id: &OwnedRoomId) -> anyhow::Result<Vec<RoomMember>> {
let client = self.client.as_ref().unwrap();
if let Some(room) = client.get_room(room_id) {
match room.members(RoomMemberships::ACTIVE).await {
Ok(room_members) => Ok(room_members),
Err(err) => Err(err.into()),
}
} else {
warn!("No room found with the \"{}\" id", room_id.as_str());
// TODO: Return an error if the room has not been found
Ok(vec![])
}
}
// TODO: Share MediaRequest with other media requests // TODO: Share MediaRequest with other media requests
async fn get_thumbnail(&self, media_url: OwnedMxcUri) -> anyhow::Result<Vec<u8>> { async fn get_thumbnail(&self, media_url: OwnedMxcUri) -> anyhow::Result<Vec<u8>> {
let client = self.client.as_ref().unwrap(); let client = self.client.as_ref().unwrap();
@@ -738,41 +723,39 @@ impl Client {
async fn get_room_member_avatar( async fn get_room_member_avatar(
&self, &self,
avatar_url: &Option<OwnedMxcUri>,
room_id: &RoomId, room_id: &RoomId,
user_id: &UserId, user_id: &UserId,
avatar_url: &Option<OwnedMxcUri>,
) -> anyhow::Result<Option<Vec<u8>>> { ) -> anyhow::Result<Option<Vec<u8>>> {
let client = self.client.as_ref().unwrap(); let client = self.client.as_ref().unwrap();
if let Some(room) = client.get_room(room_id) { if let Some(room) = client.get_room(room_id) {
// TODO: Check if we can get member before fetching the data and received an error... match avatar_url {
Some(avatar_url) => {
match room.get_member(user_id).await { let thumbnail = self.get_thumbnail(avatar_url.clone()).await;
Ok(room_member) => { return Ok(Some(thumbnail?));
if let Some(room_member) = room_member {
let res = match room_member
.avatar(MediaFormat::Thumbnail(MediaThumbnailSize {
method: Method::Scale,
width: uint!(256),
height: uint!(256),
}))
.await
{
Ok(avatar) => Ok(avatar),
Err(err) => Err(err.into()),
};
return res;
}
} }
Err(err) => { None => match room.get_member(user_id).await {
warn!("Unable to get room member {user_id}: {err}"); Ok(room_member) => {
if let Some(avatar_url) = avatar_url { if let Some(room_member) = room_member {
let thumbnail = self.get_thumbnail(avatar_url.clone()).await; let res = match room_member
return Ok(Some(thumbnail?)); .avatar(MediaFormat::Thumbnail(MediaThumbnailSize {
} else { method: Method::Scale,
debug!("No avatar url set for the {room_id} room"); width: uint!(256),
height: uint!(256),
}))
.await
{
Ok(avatar) => Ok(avatar),
Err(err) => Err(err.into()),
};
return res;
}
} }
} Err(err) => {
warn!("Unable to get room member {user_id}: {err}");
}
},
} }
} }
Ok(None) Ok(None)
@@ -814,14 +797,10 @@ impl Client {
WorkerTask::GetRoomAvatar(id, reply) => { WorkerTask::GetRoomAvatar(id, reply) => {
reply.send(self.get_room_avatar(&id).await).await; reply.send(self.get_room_avatar(&id).await).await;
} }
WorkerTask::GetRoomMembers(id, reply) => { WorkerTask::GetRoomMemberAvatar(avatar_url, room_id, user_id, reply) => {
reply.send(self.get_room_members(&id).await).await;
}
WorkerTask::GetRoomMemberAvatar(room_id, user_id, avatar_url, reply) => {
reply reply
.send( .send(
self.get_room_member_avatar(&room_id, &user_id, &avatar_url) self.get_room_member_avatar(&avatar_url, &room_id, &user_id)
.await, .await,
) )
.await; .await;

View File

@@ -242,7 +242,7 @@ impl AccountMessagingProviderInterface for Requester {
new_room_tx, new_room_tx,
span span
) => { ) => {
let mut room = Room::new(id, spaces, name, topic, is_direct, Some(state)); let mut room = Room::new(id, name, topic, is_direct, Some(state), spaces);
let room_id = room.id().clone(); let room_id = room.id().clone();
room.set_messaging_provider(client.clone()); room.set_messaging_provider(client.clone());
@@ -362,19 +362,6 @@ impl RoomMessagingProviderInterface for Requester {
async fn get_avatar(&self, room_id: &RoomId) -> anyhow::Result<Option<Avatar>> { async fn get_avatar(&self, room_id: &RoomId) -> anyhow::Result<Option<Avatar>> {
request_to_worker!(self, WorkerTask::GetRoomAvatar, room_id.clone()) request_to_worker!(self, WorkerTask::GetRoomAvatar, room_id.clone())
} }
// TODO: Fix return code
async fn get_members(&self, room_id: &RoomId) -> anyhow::Result<Vec<RoomMember>> {
match request_to_worker!(self, WorkerTask::GetRoomMembers, room_id.clone()) {
Ok(matrix_room_members) => {
Ok(join_all(matrix_room_members.iter().map(|member| async {
RoomMember::from_matrix(member, room_id, Rc::new(self.clone())).await
}))
.await)
}
Err(err) => Err(err),
}
}
} }
#[async_trait(?Send)] #[async_trait(?Send)]
@@ -384,16 +371,16 @@ impl SpaceMessagingProviderInterface for Requester {}
impl MemberMessagingProviderInterface for Requester { impl MemberMessagingProviderInterface for Requester {
async fn get_avatar( async fn get_avatar(
&self, &self,
room_id: &RoomId, avatar_url: Option<AvatarUrl>,
user_id: &UserId, room_id: RoomId,
avatar_url: &Option<AvatarUrl>, user_id: UserId,
) -> anyhow::Result<Option<Avatar>> { ) -> anyhow::Result<Option<Avatar>> {
request_to_worker!( request_to_worker!(
self, self,
WorkerTask::GetRoomMemberAvatar, WorkerTask::GetRoomMemberAvatar,
room_id.clone(), avatar_url,
user_id.clone(), room_id,
avatar_url.clone() user_id
) )
} }
} }

View File

@@ -1,9 +1,6 @@
use std::fmt::{Debug, Formatter}; use std::fmt::{Debug, Formatter};
use matrix_sdk::{ use matrix_sdk::ruma::{OwnedMxcUri, OwnedRoomId, OwnedUserId};
room::RoomMember,
ruma::{OwnedMxcUri, OwnedRoomId, OwnedUserId},
};
use crate::utils::Sender; use crate::utils::Sender;
@@ -20,12 +17,10 @@ pub enum WorkerTask {
GetAvatar(Sender<anyhow::Result<Option<Vec<u8>>>>), GetAvatar(Sender<anyhow::Result<Option<Vec<u8>>>>),
GetRoomAvatar(OwnedRoomId, Sender<anyhow::Result<Option<Vec<u8>>>>), GetRoomAvatar(OwnedRoomId, Sender<anyhow::Result<Option<Vec<u8>>>>),
GetRoomMembers(OwnedRoomId, Sender<anyhow::Result<Vec<RoomMember>>>),
GetRoomMemberAvatar( GetRoomMemberAvatar(
Option<OwnedMxcUri>,
OwnedRoomId, OwnedRoomId,
OwnedUserId, OwnedUserId,
Option<OwnedMxcUri>,
Sender<anyhow::Result<Option<Vec<u8>>>>, Sender<anyhow::Result<Option<Vec<u8>>>>,
), ),
} }
@@ -60,16 +55,11 @@ impl Debug for WorkerTask {
.debug_tuple("WorkerTask::GetRoomAvatar") .debug_tuple("WorkerTask::GetRoomAvatar")
.field(id) .field(id)
.finish(), .finish(),
WorkerTask::GetRoomMembers(id, _) => f
.debug_tuple("WorkerTask::GetRoomMembers")
.field(id)
.finish(),
WorkerTask::GetRoomMemberAvatar(room_id, user_id, avatar_url, _) => f WorkerTask::GetRoomMemberAvatar(room_id, user_id, avatar_url, _) => f
.debug_tuple("WorkerTask::GetRoomMemberAvatar") .debug_tuple("WorkerTask::GetRoomMemberAvatar")
.field(avatar_url)
.field(room_id) .field(room_id)
.field(user_id) .field(user_id)
.field(avatar_url)
.finish(), .finish(),
} }
} }