base_client: if unsigned field contains prev_content pull out and add to MemberEvent
parent
1d9fccdc9f
commit
eedf4e72d1
|
@ -31,6 +31,7 @@ use crate::events::presence::PresenceEvent;
|
||||||
use crate::events::collections::only::Event as NonRoomEvent;
|
use crate::events::collections::only::Event as NonRoomEvent;
|
||||||
use crate::events::ignored_user_list::IgnoredUserListEvent;
|
use crate::events::ignored_user_list::IgnoredUserListEvent;
|
||||||
use crate::events::push_rules::{PushRulesEvent, Ruleset};
|
use crate::events::push_rules::{PushRulesEvent, Ruleset};
|
||||||
|
use crate::events::room::member::MemberEventContent;
|
||||||
use crate::events::stripped::AnyStrippedStateEvent;
|
use crate::events::stripped::AnyStrippedStateEvent;
|
||||||
use crate::events::EventJson;
|
use crate::events::EventJson;
|
||||||
use crate::identifiers::{RoomId, UserId};
|
use crate::identifiers::{RoomId, UserId};
|
||||||
|
@ -60,6 +61,38 @@ use matrix_sdk_crypto::{OlmMachine, OneTimeKeys};
|
||||||
|
|
||||||
pub type Token = String;
|
pub type Token = String;
|
||||||
|
|
||||||
|
/// A deserialization wrapper for extracting the prev_content field when
|
||||||
|
/// found in an `unsigned` field.
|
||||||
|
///
|
||||||
|
/// Represents the outer `unsigned` field
|
||||||
|
#[derive(serde::Deserialize)]
|
||||||
|
pub struct AdditionalEventData {
|
||||||
|
unsigned: AdditionalUnsignedData,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A deserialization wrapper for extracting the prev_content field when
|
||||||
|
/// found in an `unsigned` field.
|
||||||
|
///
|
||||||
|
/// Represents the inner `prev_content` field
|
||||||
|
#[derive(serde::Deserialize)]
|
||||||
|
pub struct AdditionalUnsignedData {
|
||||||
|
pub prev_content: Option<EventJson<MemberEventContent>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn room_event_deserializer(event: &EventJson<RoomEvent>) -> Option<AdditionalUnsignedData> {
|
||||||
|
serde_json::from_str::<AdditionalEventData>(event.json().get())
|
||||||
|
.map(|more_unsigned| more_unsigned.unsigned)
|
||||||
|
.ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stripped_event_deserializer(
|
||||||
|
event: &EventJson<AnyStrippedStateEvent>,
|
||||||
|
) -> Option<AdditionalUnsignedData> {
|
||||||
|
serde_json::from_str::<AdditionalEventData>(event.json().get())
|
||||||
|
.map(|more_unsigned| more_unsigned.unsigned)
|
||||||
|
.ok()
|
||||||
|
}
|
||||||
|
|
||||||
/// Signals to the `BaseClient` which `RoomState` to send to `EventEmitter`.
|
/// Signals to the `BaseClient` which `RoomState` to send to `EventEmitter`.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum RoomStateType {
|
pub enum RoomStateType {
|
||||||
|
@ -782,7 +815,17 @@ impl BaseClient {
|
||||||
*event = e;
|
*event = e;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(e) = event.deserialize() {
|
if let Ok(mut e) = event.deserialize() {
|
||||||
|
// if the event is a m.room.member event the server will sometimes
|
||||||
|
// send the `prev_content` field as part of the unsigned field.
|
||||||
|
if let RoomEvent::RoomMember(member) = &mut e {
|
||||||
|
if let Some(raw_content) = room_event_deserializer(event) {
|
||||||
|
member.prev_content = match raw_content.prev_content {
|
||||||
|
Some(json) => json.deserialize().ok(),
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
self.emit_timeline_event(&room_id, &e, RoomStateType::Joined)
|
self.emit_timeline_event(&room_id, &e, RoomStateType::Joined)
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
@ -873,7 +916,17 @@ impl BaseClient {
|
||||||
updated = true;
|
updated = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Ok(e) = event.deserialize() {
|
if let Ok(mut e) = event.deserialize() {
|
||||||
|
// if the event is a m.room.member event the server will sometimes
|
||||||
|
// send the `prev_content` field as part of the unsigned field.
|
||||||
|
if let RoomEvent::RoomMember(member) = &mut e {
|
||||||
|
if let Some(raw_content) = room_event_deserializer(event) {
|
||||||
|
member.prev_content = match raw_content.prev_content {
|
||||||
|
Some(json) => json.deserialize().ok(),
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
self.emit_timeline_event(&room_id, &e, RoomStateType::Left)
|
self.emit_timeline_event(&room_id, &e, RoomStateType::Left)
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
@ -909,8 +962,26 @@ impl BaseClient {
|
||||||
};
|
};
|
||||||
|
|
||||||
for event in &invited_room.invite_state.events {
|
for event in &invited_room.invite_state.events {
|
||||||
if let Ok(e) = event.deserialize() {
|
if let Ok(mut e) = event.deserialize() {
|
||||||
self.emit_stripped_state_event(&room_id, &e, RoomStateType::Invited)
|
// if the event is a m.room.member event the server will sometimes
|
||||||
|
// send the `prev_content` field as part of the unsigned field.
|
||||||
|
if let AnyStrippedStateEvent::RoomMember(_) = &mut e {
|
||||||
|
if let Some(raw_content) = stripped_event_deserializer(event) {
|
||||||
|
let prev_content = match raw_content.prev_content {
|
||||||
|
Some(json) => json.deserialize().ok(),
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
self.emit_stripped_state_event(
|
||||||
|
&room_id,
|
||||||
|
&e,
|
||||||
|
prev_content,
|
||||||
|
RoomStateType::Invited,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.emit_stripped_state_event(&room_id, &e, None, RoomStateType::Invited)
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1254,6 +1325,7 @@ impl BaseClient {
|
||||||
&self,
|
&self,
|
||||||
room_id: &RoomId,
|
room_id: &RoomId,
|
||||||
event: &AnyStrippedStateEvent,
|
event: &AnyStrippedStateEvent,
|
||||||
|
prev_content: Option<MemberEventContent>,
|
||||||
room_state: RoomStateType,
|
room_state: RoomStateType,
|
||||||
) {
|
) {
|
||||||
let lock = self.event_emitter.read().await;
|
let lock = self.event_emitter.read().await;
|
||||||
|
@ -1289,7 +1361,9 @@ impl BaseClient {
|
||||||
|
|
||||||
match event {
|
match event {
|
||||||
AnyStrippedStateEvent::RoomMember(member) => {
|
AnyStrippedStateEvent::RoomMember(member) => {
|
||||||
event_emitter.on_stripped_state_member(room, &member).await
|
event_emitter
|
||||||
|
.on_stripped_state_member(room, &member, prev_content)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
AnyStrippedStateEvent::RoomName(name) => {
|
AnyStrippedStateEvent::RoomName(name) => {
|
||||||
event_emitter.on_stripped_state_name(room, &name).await
|
event_emitter.on_stripped_state_name(room, &name).await
|
||||||
|
@ -1626,6 +1700,86 @@ mod test {
|
||||||
assert!(room.is_some());
|
assert!(room.is_some());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_test]
|
||||||
|
async fn test_prev_content_from_unsigned() {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
use crate::{EventEmitter, SyncRoom};
|
||||||
|
use matrix_sdk_common::events::{
|
||||||
|
room::member::{MemberEvent, MembershipChange},
|
||||||
|
EventJson,
|
||||||
|
};
|
||||||
|
use matrix_sdk_common::locks::RwLock;
|
||||||
|
use std::sync::{
|
||||||
|
atomic::{AtomicBool, Ordering},
|
||||||
|
Arc,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct EE(Arc<AtomicBool>);
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl EventEmitter for EE {
|
||||||
|
async fn on_room_member(&self, room: SyncRoom, event: &MemberEvent) {
|
||||||
|
if let SyncRoom::Joined(_) = room {
|
||||||
|
println!("joined");
|
||||||
|
match event.membership_change() {
|
||||||
|
MembershipChange::Joined => {
|
||||||
|
self.0.swap(true, Ordering::SeqCst);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if event.prev_content.is_none() {
|
||||||
|
println!("NOT found");
|
||||||
|
self.0.swap(false, Ordering::SeqCst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let room_id = get_room_id();
|
||||||
|
let passed = Arc::new(AtomicBool::default());
|
||||||
|
let emitter = EE(Arc::clone(&passed));
|
||||||
|
let mut client = get_client();
|
||||||
|
|
||||||
|
// fake a room event with no EventEmitter to build the correct Room
|
||||||
|
let mut sync_response = EventBuilder::default()
|
||||||
|
.add_room_event(EventsFile::Member, RoomEvent::RoomMember)
|
||||||
|
.build_sync_response();
|
||||||
|
client
|
||||||
|
.receive_sync_response(&mut sync_response)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
client.event_emitter = Arc::new(RwLock::new(Some(Box::new(emitter))));
|
||||||
|
|
||||||
|
let event = serde_json::from_str::<EventJson<RoomEvent>>(include_str!(
|
||||||
|
"../../test_data/events/member.json"
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// FIXME since the `SyncResponse` created using EventBuilder doesn't pull out the `prev_content`
|
||||||
|
// from the `unsigned` field we must do this manually.
|
||||||
|
//
|
||||||
|
// this is exactly what happens in `receive_sync_response`
|
||||||
|
if let Ok(mut e) = event.deserialize() {
|
||||||
|
// if the event is a m.room.member event the server will sometimes
|
||||||
|
// send the `prev_content` field as part of the unsigned field.
|
||||||
|
if let RoomEvent::RoomMember(member) = &mut e {
|
||||||
|
if let Some(raw_content) = room_event_deserializer(&event) {
|
||||||
|
member.prev_content = match raw_content.prev_content {
|
||||||
|
Some(json) => json.deserialize().ok(),
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
println!("{:?}", member.prev_content);
|
||||||
|
}
|
||||||
|
client
|
||||||
|
.emit_timeline_event(&room_id, &e, RoomStateType::Joined)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert!(passed.load(Ordering::SeqCst))
|
||||||
|
}
|
||||||
|
|
||||||
#[async_test]
|
#[async_test]
|
||||||
#[cfg(feature = "encryption")]
|
#[cfg(feature = "encryption")]
|
||||||
async fn test_group_session_invalidation() {
|
async fn test_group_session_invalidation() {
|
||||||
|
|
|
@ -27,7 +27,7 @@ use crate::events::{
|
||||||
avatar::AvatarEvent,
|
avatar::AvatarEvent,
|
||||||
canonical_alias::CanonicalAliasEvent,
|
canonical_alias::CanonicalAliasEvent,
|
||||||
join_rules::JoinRulesEvent,
|
join_rules::JoinRulesEvent,
|
||||||
member::MemberEvent,
|
member::{MemberEvent, MemberEventContent},
|
||||||
message::{feedback::FeedbackEvent, MessageEvent},
|
message::{feedback::FeedbackEvent, MessageEvent},
|
||||||
name::NameEvent,
|
name::NameEvent,
|
||||||
power_levels::PowerLevelsEvent,
|
power_levels::PowerLevelsEvent,
|
||||||
|
@ -131,7 +131,13 @@ pub trait EventEmitter: Send + Sync {
|
||||||
|
|
||||||
// `AnyStrippedStateEvent`s
|
// `AnyStrippedStateEvent`s
|
||||||
/// Fires when `Client` receives a `AnyStrippedStateEvent::StrippedRoomMember` event.
|
/// Fires when `Client` receives a `AnyStrippedStateEvent::StrippedRoomMember` event.
|
||||||
async fn on_stripped_state_member(&self, _: SyncRoom, _: &StrippedRoomMember) {}
|
async fn on_stripped_state_member(
|
||||||
|
&self,
|
||||||
|
_: SyncRoom,
|
||||||
|
_: &StrippedRoomMember,
|
||||||
|
_: Option<MemberEventContent>,
|
||||||
|
) {
|
||||||
|
}
|
||||||
/// Fires when `Client` receives a `AnyStrippedStateEvent::StrippedRoomName` event.
|
/// Fires when `Client` receives a `AnyStrippedStateEvent::StrippedRoomName` event.
|
||||||
async fn on_stripped_state_name(&self, _: SyncRoom, _: &StrippedRoomName) {}
|
async fn on_stripped_state_name(&self, _: SyncRoom, _: &StrippedRoomName) {}
|
||||||
/// Fires when `Client` receives a `AnyStrippedStateEvent::StrippedRoomCanonicalAlias` event.
|
/// Fires when `Client` receives a `AnyStrippedStateEvent::StrippedRoomCanonicalAlias` event.
|
||||||
|
@ -235,7 +241,12 @@ mod test {
|
||||||
self.0.lock().await.push("state rules".to_string())
|
self.0.lock().await.push("state rules".to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn on_stripped_state_member(&self, _: SyncRoom, _: &StrippedRoomMember) {
|
async fn on_stripped_state_member(
|
||||||
|
&self,
|
||||||
|
_: SyncRoom,
|
||||||
|
_: &StrippedRoomMember,
|
||||||
|
_: Option<MemberEventContent>,
|
||||||
|
) {
|
||||||
self.0
|
self.0
|
||||||
.lock()
|
.lock()
|
||||||
.await
|
.await
|
||||||
|
|
|
@ -12,6 +12,11 @@
|
||||||
"type": "m.room.member",
|
"type": "m.room.member",
|
||||||
"unsigned": {
|
"unsigned": {
|
||||||
"age": 2970366338,
|
"age": 2970366338,
|
||||||
"replaces_state": "$151800111315tsynI:localhost"
|
"replaces_state": "$151800111315tsynI:localhost",
|
||||||
|
"prev_content": {
|
||||||
|
"avatar_url": null,
|
||||||
|
"displayname": "example",
|
||||||
|
"membership": "invite"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue