diff --git a/src/base.rs b/src/base.rs index 0661a4d..6cc4283 100644 --- a/src/base.rs +++ b/src/base.rs @@ -131,6 +131,24 @@ pub enum MessageAction { Unreact(Option), } +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum CreateRoomType { + /// A direct message room. + Direct(OwnedUserId), +} + +bitflags::bitflags! { + pub struct CreateRoomFlags: u32 { + const NONE = 0b00000000; + + /// Make the room public. + const PUBLIC = 0b00000001; + + /// Encrypt this room. + const ENCRYPTED = 0b00000010; + } +} + bitflags::bitflags! { pub struct DownloadFlags: u32 { const NONE = 0b00000000; diff --git a/src/windows/room/mod.rs b/src/windows/room/mod.rs index 9e59b54..eac9027 100644 --- a/src/windows/room/mod.rs +++ b/src/windows/room/mod.rs @@ -185,8 +185,16 @@ impl RoomState { match act { RoomAction::InviteAccept => { if let Some(room) = store.application.worker.client.get_invited_room(self.id()) { + let details = room.invite_details().await.map_err(IambError::from)?; + let details = details.invitee.event().original_content(); + let is_direct = details.and_then(|ev| ev.is_direct).unwrap_or_default(); + room.accept_invitation().await.map_err(IambError::from)?; + if is_direct { + room.set_is_direct(true).await.map_err(IambError::from)?; + } + Ok(vec![]) } else { Err(IambError::NotInvited.into()) diff --git a/src/worker.rs b/src/worker.rs index dc55977..5707df8 100644 --- a/src/worker.rs +++ b/src/worker.rs @@ -24,6 +24,7 @@ use matrix_sdk::{ room::Visibility, space::get_hierarchy::v1::Request as SpaceHierarchyRequest, }, + assign, events::{ key::verification::{ done::{OriginalSyncKeyVerificationDoneEvent, ToDeviceKeyVerificationDoneEvent}, @@ -35,16 +36,22 @@ use matrix_sdk::{ presence::PresenceEvent, reaction::ReactionEventContent, room::{ + encryption::RoomEncryptionEventContent, message::{MessageType, RoomMessageEventContent}, name::RoomNameEventContent, redaction::{OriginalSyncRoomRedactionEvent, SyncRoomRedactionEvent}, }, tag::Tags, typing::SyncTypingEvent, + AnyInitialStateEvent, AnyTimelineEvent, + EmptyStateKey, + InitialStateEvent, SyncMessageLikeEvent, SyncStateEvent, }, + serde::Raw, + EventEncryptionAlgorithm, OwnedRoomId, OwnedRoomOrAliasId, OwnedUserId, @@ -58,7 +65,16 @@ use matrix_sdk::{ use modalkit::editing::action::{EditInfo, InfoMessage, UIError}; use crate::{ - base::{AsyncProgramStore, EventLocation, IambError, IambResult, Receipts, VerifyAction}, + base::{ + AsyncProgramStore, + CreateRoomFlags, + CreateRoomType, + EventLocation, + IambError, + IambResult, + Receipts, + VerifyAction, + }, message::MessageFetchResult, ApplicationSettings, }; @@ -71,6 +87,69 @@ fn initial_devname() -> String { format!("{} on {}", IAMB_DEVICE_NAME, gethostname().to_string_lossy()) } +pub async fn create_room( + client: &Client, + room_alias_name: Option<&str>, + rt: CreateRoomType, + flags: CreateRoomFlags, +) -> IambResult { + let creation_content = None; + let mut initial_state = vec![]; + let is_direct; + let preset; + let mut invite = vec![]; + + let visibility = if flags.contains(CreateRoomFlags::PUBLIC) { + Visibility::Public + } else { + Visibility::Private + }; + + match rt { + CreateRoomType::Direct(user) => { + invite.push(user); + is_direct = true; + preset = Some(RoomPreset::TrustedPrivateChat); + }, + } + + // Set up encryption. + if flags.contains(CreateRoomFlags::ENCRYPTED) { + // XXX: Once matrix-sdk uses ruma 0.8, then this can skip the cast. + let algo = EventEncryptionAlgorithm::MegolmV1AesSha2; + let content = RoomEncryptionEventContent::new(algo); + let encr = InitialStateEvent { content, state_key: EmptyStateKey }; + let encr_raw = Raw::new(&encr).map_err(IambError::from)?; + let encr_raw = encr_raw.cast::(); + initial_state.push(encr_raw); + } + + let request = assign!(CreateRoomRequest::new(), { + room_alias_name, + creation_content, + initial_state: initial_state.as_slice(), + invite: invite.as_slice(), + is_direct, + visibility, + preset, + }); + + let resp = client.create_room(request).await.map_err(IambError::from)?; + + if is_direct { + if let Some(room) = client.get_room(&resp.room_id) { + room.set_is_direct(true).await.map_err(IambError::from)?; + } else { + error!( + room_id = resp.room_id.as_str(), + "Couldn't set is_direct for new direct message room" + ); + } + } + + return Ok(resp.room_id); +} + #[derive(Debug)] pub enum LoginStyle { SessionRestore(Session), @@ -788,15 +867,11 @@ impl ClientWorker { } } - let mut request = CreateRoomRequest::new(); - let invite = [user.clone()]; - request.is_direct = true; - request.invite = &invite; - request.visibility = Visibility::Private; - request.preset = Some(RoomPreset::PrivateChat); + let rt = CreateRoomType::Direct(user.clone()); + let flags = CreateRoomFlags::ENCRYPTED; - match self.client.create_room(request).await { - Ok(resp) => self.get_room(resp.room_id).await, + match create_room(&self.client, None, rt, flags).await { + Ok(room_id) => self.get_room(room_id).await, Err(e) => { error!( user_id = user.as_str(),