324 lines
9.2 KiB
Rust
324 lines
9.2 KiB
Rust
use std::{
|
|
cell::RefCell,
|
|
collections::HashMap,
|
|
fmt::{Debug, Formatter},
|
|
rc::Rc,
|
|
};
|
|
|
|
use async_trait::async_trait;
|
|
use futures::future::{join, join_all};
|
|
use matrix_sdk::{ruma::OwnedRoomId, RoomState as MatrixRoomState};
|
|
use tracing::{debug, debug_span, error, instrument, trace};
|
|
|
|
use super::{
|
|
common::{Avatar, UserId},
|
|
messaging_interface::{RoomMessagingConsumerInterface, RoomMessagingProviderInterface},
|
|
room_member::RoomMember,
|
|
space::SpaceId,
|
|
store_interface::{RoomStoreConsumerInterface, RoomStoreProviderInterface},
|
|
};
|
|
use crate::infrastructure::services::mozaik_builder::create_mozaik;
|
|
|
|
pub type RoomId = OwnedRoomId;
|
|
|
|
#[derive(Clone, PartialEq)]
|
|
pub struct Invitation {
|
|
invitee_id: UserId,
|
|
sender_id: UserId,
|
|
is_account_user: bool,
|
|
}
|
|
|
|
impl Invitation {
|
|
pub fn new(invitee_id: UserId, sender_id: UserId, is_account_user: bool) -> Self {
|
|
Self {
|
|
invitee_id,
|
|
sender_id,
|
|
is_account_user,
|
|
}
|
|
}
|
|
|
|
pub fn is_account_user(&self) -> bool {
|
|
self.is_account_user
|
|
}
|
|
}
|
|
|
|
impl Debug for Invitation {
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
|
|
f.debug_tuple("Invitation")
|
|
.field(&self.invitee_id)
|
|
.field(&self.sender_id)
|
|
.field(&self.is_account_user)
|
|
.finish()
|
|
}
|
|
}
|
|
|
|
pub struct Room {
|
|
id: RoomId,
|
|
|
|
name: RefCell<Option<String>>,
|
|
topic: Option<String>,
|
|
is_direct: Option<bool>,
|
|
state: Option<MatrixRoomState>,
|
|
avatar: RefCell<Option<Avatar>>,
|
|
|
|
invitations: RefCell<HashMap<UserId, Invitation>>,
|
|
members: RefCell<HashMap<UserId, RoomMember>>,
|
|
|
|
spaces: Vec<SpaceId>,
|
|
|
|
messaging_provider: Option<Rc<dyn RoomMessagingProviderInterface>>,
|
|
store: RefCell<Option<Rc<dyn RoomStoreProviderInterface>>>,
|
|
}
|
|
|
|
impl PartialEq for Room {
|
|
fn eq(&self, other: &Self) -> bool {
|
|
self.id == other.id
|
|
}
|
|
}
|
|
|
|
impl Room {
|
|
pub fn new(
|
|
id: RoomId,
|
|
// TODO: move space at the end of the list of params
|
|
name: Option<String>,
|
|
topic: Option<String>,
|
|
is_direct: Option<bool>,
|
|
state: Option<MatrixRoomState>,
|
|
spaces: Vec<SpaceId>,
|
|
) -> Self {
|
|
Self {
|
|
id,
|
|
|
|
name: RefCell::new(name),
|
|
topic,
|
|
is_direct,
|
|
state,
|
|
avatar: RefCell::new(None),
|
|
invitations: RefCell::new(HashMap::new()),
|
|
members: RefCell::new(HashMap::new()),
|
|
spaces,
|
|
|
|
messaging_provider: None,
|
|
store: RefCell::new(None),
|
|
}
|
|
}
|
|
|
|
pub fn set_messaging_provider(
|
|
&mut self,
|
|
messaging_provider: Rc<dyn RoomMessagingProviderInterface>,
|
|
) {
|
|
self.messaging_provider = Some(messaging_provider);
|
|
}
|
|
|
|
pub fn set_store(&self, store: Rc<dyn RoomStoreProviderInterface>) {
|
|
*self.store.borrow_mut() = Some(store);
|
|
}
|
|
|
|
pub fn id(&self) -> &RoomId {
|
|
&self.id
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
pub fn name(&self) -> Option<String> {
|
|
self.name.borrow().clone()
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
pub fn set_topic(&mut self, topic: Option<String>) {
|
|
self.topic = topic;
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
pub fn state(&self) -> &Option<MatrixRoomState> {
|
|
&self.state
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
pub fn is_invited(&self) -> Option<bool> {
|
|
self.state.map(|state| state == MatrixRoomState::Invited)
|
|
}
|
|
|
|
#[instrument(name = "Room", skip_all)]
|
|
fn add_invitation(&self, invitation: Invitation) {
|
|
self.members.borrow_mut().remove(&invitation.invitee_id);
|
|
|
|
self.invitations
|
|
.borrow_mut()
|
|
.insert(invitation.invitee_id.clone(), invitation.clone());
|
|
|
|
if let Some(store) = self.store.borrow().as_ref() {
|
|
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> {
|
|
if self.avatar.borrow().is_none() {
|
|
if let Some(requester) = &self.messaging_provider {
|
|
let resp = requester.get_avatar(&self.id).await;
|
|
if let Ok(avatar) = resp {
|
|
if let Some(avatar) = avatar {
|
|
return Some(avatar);
|
|
} else {
|
|
debug!("The room has no avatar... let's generate one");
|
|
match self.gen_room_avatar_with_members().await {
|
|
Ok(avatar) => {
|
|
if let Some(avatar) = avatar {
|
|
return Some(avatar);
|
|
}
|
|
}
|
|
Err(err) => {
|
|
error!("err={}", err);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
error!("err={:?}", resp);
|
|
}
|
|
}
|
|
}
|
|
self.avatar.borrow().clone()
|
|
}
|
|
|
|
#[instrument(name = "Room", skip_all)]
|
|
async fn gen_room_avatar_with_members(&self) -> anyhow::Result<Option<Avatar>> {
|
|
let mut account_member = None::<&RoomMember>;
|
|
let mut other_members = Vec::<&RoomMember>::new();
|
|
|
|
let members = self.members.borrow();
|
|
for member in members.values() {
|
|
if member.is_account_user() {
|
|
account_member = Some(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();
|
|
|
|
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)
|
|
}
|
|
}
|
|
}
|
|
|
|
#[async_trait(?Send)]
|
|
impl RoomMessagingConsumerInterface for Room {
|
|
#[instrument(name = "Room", skip_all)]
|
|
async fn on_invitation(&self, invitation: Invitation) {
|
|
trace!("on_invitation");
|
|
let sender_id = invitation.sender_id.clone();
|
|
|
|
self.add_invitation(invitation);
|
|
|
|
if self.is_direct.unwrap_or(false) {
|
|
debug!("1to1 conversation, using the {} avatar", &sender_id);
|
|
if let Ok(avatar) = self.gen_room_avatar_with_members().await {
|
|
debug!("Avatar successfully generated");
|
|
self.avatar.borrow_mut().clone_from(&avatar);
|
|
if let Some(store) = self.store.borrow().as_ref() {
|
|
store.on_new_avatar(avatar);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[instrument(name = "Room", skip_all)]
|
|
async fn on_membership(&self, member: RoomMember) {
|
|
trace!("on_membership");
|
|
self.add_member(member);
|
|
}
|
|
|
|
#[instrument(name = "Room", skip_all)]
|
|
async fn on_new_topic(&self, _topic: Option<String>) {
|
|
trace!("on_new_topic");
|
|
}
|
|
|
|
#[instrument(name = "Room", skip_all)]
|
|
async fn on_new_name(&self, _name: Option<String>) {
|
|
trace!("on_new_name");
|
|
}
|
|
|
|
#[instrument(name = "Room", skip_all)]
|
|
async fn on_new_avatar(&self, avatar: Option<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);
|
|
}
|
|
}
|
|
}
|
|
|
|
#[async_trait(?Send)]
|
|
impl RoomStoreConsumerInterface for Room {
|
|
fn id(&self) -> &RoomId {
|
|
&self.id
|
|
}
|
|
|
|
fn is_direct(&self) -> Option<bool> {
|
|
self.is_direct
|
|
}
|
|
|
|
fn name(&self) -> Option<String> {
|
|
self.name.borrow().clone()
|
|
}
|
|
|
|
fn topic(&self) -> Option<String> {
|
|
self.topic.clone()
|
|
}
|
|
|
|
async fn avatar(&self) -> Option<Avatar> {
|
|
self.get_avatar().await
|
|
}
|
|
|
|
fn spaces(&self) -> &Vec<SpaceId> {
|
|
&self.spaces
|
|
}
|
|
|
|
async fn join(&self) {
|
|
if let Some(messaging_provider) = &self.messaging_provider {
|
|
let _ = messaging_provider.join(&self.id).await;
|
|
}
|
|
}
|
|
}
|