mirror of
https://github.com/matrix-org/matrix-rust-sdk.git
synced 2026-05-08 16:04:13 -04:00
feat(sdk): Add method to set whether a room is DM and store all targets
This ensures also that we use, for user verification, only a DM room with no members other then ourself and the user to be verified.
This commit is contained in:
@@ -497,11 +497,12 @@ impl BaseClient {
|
||||
);
|
||||
|
||||
if let Some(room) = changes.room_infos.get_mut(room_id) {
|
||||
room.base_info.dm_target = Some(user_id.clone());
|
||||
room.base_info.dm_targets.insert(user_id.clone());
|
||||
} else if let Some(room) = self.store.get_room(room_id) {
|
||||
let mut info = room.clone_info();
|
||||
info.base_info.dm_target = Some(user_id.clone());
|
||||
changes.add_room(info);
|
||||
if info.base_info.dm_targets.insert(user_id.clone()) {
|
||||
changes.add_room(info);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
mod members;
|
||||
mod normal;
|
||||
|
||||
use std::cmp::max;
|
||||
use std::{cmp::max, collections::HashSet};
|
||||
|
||||
pub use members::RoomMember;
|
||||
pub use normal::{Room, RoomInfo, RoomType};
|
||||
@@ -29,9 +29,9 @@ pub struct BaseRoomInfo {
|
||||
pub(crate) canonical_alias: Option<OwnedRoomAliasId>,
|
||||
/// The `m.room.create` event content of this room.
|
||||
pub(crate) create: Option<RoomCreateEventContent>,
|
||||
/// The user id this room is sharing the direct message with, if the room is
|
||||
/// a direct message.
|
||||
pub(crate) dm_target: Option<OwnedUserId>,
|
||||
/// A list of user ids this room is considered as direct message, if this
|
||||
/// room is a DM.
|
||||
pub(crate) dm_targets: HashSet<OwnedUserId>,
|
||||
/// The `m.room.encryption` event content that enabled E2EE in this room.
|
||||
pub(crate) encryption: Option<RoomEncryptionEventContent>,
|
||||
/// The guest access policy of this room.
|
||||
@@ -183,7 +183,7 @@ impl Default for BaseRoomInfo {
|
||||
avatar_url: None,
|
||||
canonical_alias: None,
|
||||
create: None,
|
||||
dm_target: None,
|
||||
dm_targets: Default::default(),
|
||||
encryption: None,
|
||||
guest_access: GuestAccess::Forbidden,
|
||||
history_visibility: HistoryVisibility::WorldReadable,
|
||||
|
||||
@@ -12,7 +12,10 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use std::sync::{Arc, RwLock as SyncRwLock};
|
||||
use std::{
|
||||
collections::HashSet,
|
||||
sync::{Arc, RwLock as SyncRwLock},
|
||||
};
|
||||
|
||||
use dashmap::DashSet;
|
||||
use futures_channel::mpsc;
|
||||
@@ -184,17 +187,17 @@ impl Room {
|
||||
|
||||
/// Is this room considered a direct message.
|
||||
pub fn is_direct(&self) -> bool {
|
||||
self.inner.read().unwrap().base_info.dm_target.is_some()
|
||||
!self.inner.read().unwrap().base_info.dm_targets.is_empty()
|
||||
}
|
||||
|
||||
/// If this room is a direct message, get the member that we're sharing the
|
||||
/// If this room is a direct message, get the members that we're sharing the
|
||||
/// room with.
|
||||
///
|
||||
/// *Note*: The member list might have been modified in the meantime and
|
||||
/// the target might not even be in the room anymore. This setting should
|
||||
/// the targets might not even be in the room anymore. This setting should
|
||||
/// only be considered as guidance.
|
||||
pub fn direct_target(&self) -> Option<OwnedUserId> {
|
||||
self.inner.read().unwrap().base_info.dm_target.clone()
|
||||
pub fn direct_targets(&self) -> HashSet<OwnedUserId> {
|
||||
self.inner.read().unwrap().base_info.dm_targets.clone()
|
||||
}
|
||||
|
||||
/// Is the room encrypted.
|
||||
|
||||
@@ -465,6 +465,15 @@ impl OtherUserIdentity {
|
||||
let content = self.inner.verification_request_content(methods.clone()).await;
|
||||
|
||||
let room = if let Some(room) = self.direct_message_room.read().await.as_ref() {
|
||||
// Make sure that the user, to be verified, is still in the room
|
||||
if !room
|
||||
.active_members()
|
||||
.await?
|
||||
.iter()
|
||||
.any(|member| member.user_id() == self.inner.user_id())
|
||||
{
|
||||
room.invite_user_by_id(self.inner.user_id()).await?;
|
||||
}
|
||||
room.clone()
|
||||
} else if let Some(room) =
|
||||
self.client.create_dm_room(self.inner.user_id().to_owned()).await?
|
||||
|
||||
@@ -404,11 +404,12 @@ impl Client {
|
||||
#[cfg(feature = "encryption")]
|
||||
fn get_dm_room(&self, user_id: &UserId) -> Option<room::Joined> {
|
||||
let rooms = self.joined_rooms();
|
||||
let room_pairs: Vec<_> =
|
||||
rooms.iter().map(|r| (r.room_id().to_owned(), r.direct_target())).collect();
|
||||
trace!(rooms = ?room_pairs, "Finding direct room");
|
||||
|
||||
let room = rooms.into_iter().find(|r| r.direct_target().as_deref() == Some(user_id));
|
||||
// Find the room we share with the `user_id` and only with `user_id`
|
||||
let room = rooms.into_iter().find(|r| {
|
||||
let targets = r.direct_targets();
|
||||
targets.len() == 1 && targets.contains(user_id)
|
||||
});
|
||||
|
||||
trace!(?room, "Found room");
|
||||
room
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use std::{ops::Deref, sync::Arc};
|
||||
use std::{collections::BTreeMap, ops::Deref, sync::Arc};
|
||||
|
||||
use futures_core::stream::Stream;
|
||||
use matrix_sdk_base::{
|
||||
@@ -8,6 +8,7 @@ use matrix_sdk_base::{
|
||||
use matrix_sdk_common::locks::Mutex;
|
||||
use ruma::{
|
||||
api::client::{
|
||||
config::set_global_account_data,
|
||||
filter::{LazyLoadOptions, RoomEventFilter},
|
||||
membership::{get_member_events, join_room_by_id, leave_room},
|
||||
message::get_message_events::{self, v3::Direction},
|
||||
@@ -16,10 +17,11 @@ use ruma::{
|
||||
},
|
||||
assign,
|
||||
events::{
|
||||
direct::DirectEvent,
|
||||
room::{history_visibility::HistoryVisibility, MediaSource},
|
||||
tag::{TagInfo, TagName},
|
||||
AnyRoomAccountDataEvent, AnyStateEvent, AnySyncStateEvent, RedactContent,
|
||||
RedactedEventContent, RoomAccountDataEvent, RoomAccountDataEventContent,
|
||||
AnyRoomAccountDataEvent, AnyStateEvent, AnySyncStateEvent, GlobalAccountDataEventType,
|
||||
RedactContent, RedactedEventContent, RoomAccountDataEvent, RoomAccountDataEventContent,
|
||||
RoomAccountDataEventType, StateEventContent, StateEventType, StaticEventContent,
|
||||
SyncStateEvent,
|
||||
},
|
||||
@@ -30,7 +32,7 @@ use ruma::{
|
||||
use crate::{
|
||||
media::{MediaFormat, MediaRequest},
|
||||
room::RoomType,
|
||||
BaseRoom, Client, HttpError, HttpResult, Result, RoomMember,
|
||||
BaseRoom, Client, Error, HttpError, HttpResult, Result, RoomMember,
|
||||
};
|
||||
|
||||
/// A struct containing methods that are common for Joined, Invited and Left
|
||||
@@ -817,6 +819,56 @@ impl Common {
|
||||
let request = delete_tag::v3::Request::new(&user_id, self.inner.room_id(), tag.as_ref());
|
||||
self.client.send(request, None).await
|
||||
}
|
||||
|
||||
/// Sets whether this room is a DM.
|
||||
///
|
||||
/// When setting this room as DM, it will be marked as DM for all active
|
||||
/// members of the room. When unsetting this room as DM, it will be
|
||||
/// unmarked as DM for all users, not just the members.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `is_direct` - Whether to mark this room as direct.
|
||||
pub async fn set_is_direct(&self, is_direct: bool) -> Result<()> {
|
||||
let user_id = self
|
||||
.client
|
||||
.user_id()
|
||||
.await
|
||||
.ok_or_else(|| Error::from(HttpError::AuthenticationRequired))?;
|
||||
|
||||
let mut content = self
|
||||
.client
|
||||
.store()
|
||||
.get_account_data_event(GlobalAccountDataEventType::Direct)
|
||||
.await?
|
||||
.map(|e| e.deserialize_as::<DirectEvent>())
|
||||
.transpose()?
|
||||
.map(|e| e.content)
|
||||
.unwrap_or_else(|| ruma::events::direct::DirectEventContent(BTreeMap::new()));
|
||||
|
||||
let this_room_id = self.inner.room_id();
|
||||
|
||||
if is_direct {
|
||||
let room_members = self.active_members().await?;
|
||||
for member in room_members {
|
||||
let entry = content.entry(member.user_id().to_owned()).or_default();
|
||||
if !entry.iter().any(|room_id| room_id == this_room_id) {
|
||||
entry.push(this_room_id.to_owned());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (_, list) in content.iter_mut() {
|
||||
list.retain(|room_id| *room_id != this_room_id);
|
||||
}
|
||||
|
||||
// Remove user ids that don't have any room marked as DM
|
||||
content.retain(|_, list| !list.is_empty());
|
||||
}
|
||||
|
||||
let request = set_global_account_data::v3::Request::new(&content, &user_id)?;
|
||||
|
||||
self.client.send(request, None).await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Options for [`messages`][Common::messages].
|
||||
|
||||
Reference in New Issue
Block a user