fix: send state in /sync, element displays wrong membership changes

next
Timo Kösters 2020-12-22 20:08:20 +01:00
parent 6606e41dde
commit f12fbca3c5
No known key found for this signature in database
GPG Key ID: 24DA7517711A2BA4
4 changed files with 55 additions and 18 deletions

View File

@ -686,7 +686,7 @@ async fn join_room_by_id_helper(
pdu_id.extend_from_slice(&count.to_be_bytes());
db.rooms.append_pdu(
&PduEvent::from(&**pdu),
&utils::to_canonical_object(&**pdu).expect("Pdu is valid canonical object"),
utils::to_canonical_object(&**pdu).expect("Pdu is valid canonical object"),
count,
pdu_id.clone().into(),
&db.globals,

View File

@ -91,15 +91,7 @@ pub async fn sync_events_route(
// They /sync response doesn't always return all messages, so we say the output is
// limited unless there are events in non_timeline_pdus
let mut limited = false;
let mut state_pdus = Vec::new();
for (_, pdu) in non_timeline_pdus {
if pdu.state_key.is_some() {
state_pdus.push(pdu);
}
limited = true;
}
let limited = non_timeline_pdus.next().is_some();
// Database queries:
@ -342,7 +334,7 @@ pub async fn sync_events_route(
})?;
let room_events = timeline_pdus
.into_iter()
.iter()
.map(|(_, pdu)| pdu.to_sync_room_event())
.collect::<Vec<_>>();
@ -392,7 +384,6 @@ pub async fn sync_events_route(
prev_batch,
events: room_events,
},
// TODO: state before timeline
state: sync_events::State {
events: if joined_since_last_sync {
db.rooms
@ -401,7 +392,26 @@ pub async fn sync_events_route(
.map(|(_, pdu)| pdu.to_sync_state_event())
.collect()
} else {
Vec::new()
match since_state {
None => Vec::new(),
Some(Some(since_state)) => current_state
.iter()
.filter(|(key, value)| {
since_state.get(key).map(|e| &e.event_id) != Some(&value.event_id)
})
.filter(|(_, value)| {
!timeline_pdus.iter().any(|(_, timeline_pdu)| {
timeline_pdu.kind == value.kind
&& timeline_pdu.state_key == value.state_key
})
})
.map(|(_, pdu)| pdu.to_sync_state_event())
.collect(),
Some(None) => current_state
.iter()
.map(|(_, pdu)| pdu.to_sync_state_event())
.collect(),
}
},
},
ephemeral: sync_events::Ephemeral { events: edus },

View File

@ -15,7 +15,7 @@ use ruma::{
},
EventType,
},
serde::{to_canonical_value, CanonicalJsonObject, Raw},
serde::{to_canonical_value, CanonicalJsonObject, CanonicalJsonValue, Raw},
EventId, RoomAliasId, RoomId, RoomVersionId, ServerName, UserId,
};
use sled::IVec;
@ -444,13 +444,40 @@ impl Rooms {
pub fn append_pdu(
&self,
pdu: &PduEvent,
pdu_json: &CanonicalJsonObject,
mut pdu_json: CanonicalJsonObject,
count: u64,
pdu_id: IVec,
globals: &super::globals::Globals,
account_data: &super::account_data::AccountData,
admin: &super::admin::Admin,
) -> Result<()> {
// Make unsigned fields correct. This is not properly documented in the spec, but state
// events need to have previous content in the unsigned field, so clients can easily
// interpret things like membership changes
if let Some(state_key) = &pdu.state_key {
if let CanonicalJsonValue::Object(unsigned) = pdu_json
.entry("unsigned".to_owned())
.or_insert_with(|| CanonicalJsonValue::Object(Default::default()))
{
if let Some(prev_state_hash) = self.pdu_state_hash(&pdu_id).unwrap() {
if let Some(prev_state) = self
.state_get(&pdu.room_id, &prev_state_hash, &pdu.kind, &state_key)
.unwrap()
{
unsigned.insert(
"prev_content".to_owned(),
CanonicalJsonValue::Object(
utils::to_canonical_object(prev_state.1.content)
.expect("event is valid, we just created it"),
),
);
}
}
} else {
error!("Invalid unsigned type in pdu.");
}
}
self.replace_pdu_leaves(&pdu.room_id, &pdu.event_id)?;
// Mark as read first so the sending client doesn't get a notification even if appending
@ -460,7 +487,7 @@ impl Rooms {
self.pduid_pdu.insert(
&pdu_id,
&*serde_json::to_string(pdu_json)
&*serde_json::to_string(&pdu_json)
.expect("CanonicalJsonObject is always a valid String"),
)?;
@ -905,7 +932,7 @@ impl Rooms {
self.append_pdu(
&pdu,
&pdu_json,
pdu_json,
count,
pdu_id.clone().into(),
globals,

View File

@ -494,7 +494,7 @@ pub async fn send_transaction_message_route<'a>(
db.rooms.append_pdu(
&pdu,
&value,
value,
count,
pdu_id.clone().into(),
&db.globals,