events: Add support for transitional extensible location messages
According to MSC3488
This commit is contained in:
parent
c6d11c78a7
commit
aba6328d1c
@ -10,10 +10,25 @@ use serde::{Deserialize, Serialize};
|
|||||||
|
|
||||||
mod zoomlevel_serde;
|
mod zoomlevel_serde;
|
||||||
|
|
||||||
use super::{message::MessageContent, room::message::Relation};
|
use super::{
|
||||||
|
message::MessageContent,
|
||||||
|
room::message::{LocationMessageEventContent, Relation},
|
||||||
|
};
|
||||||
use crate::{MilliSecondsSinceUnixEpoch, PrivOwnedStr};
|
use crate::{MilliSecondsSinceUnixEpoch, PrivOwnedStr};
|
||||||
|
|
||||||
/// The payload for an extensible location message.
|
/// The payload for an extensible location message.
|
||||||
|
///
|
||||||
|
/// This is the new primary type introduced in [MSC3488] and should not be sent before the end of
|
||||||
|
/// the transition period. See the documentation of the [`message`] module for more information.
|
||||||
|
///
|
||||||
|
/// `LocationEventContent` can be converted to a [`RoomMessageEventContent`] with a
|
||||||
|
/// [`MessageType::Location`]. You can convert it back with
|
||||||
|
/// [`LocationEventContent::from_location_room_message()`].
|
||||||
|
///
|
||||||
|
/// [MSC3488]: https://github.com/matrix-org/matrix-spec-proposals/pull/3488
|
||||||
|
/// [`message`]: super::message
|
||||||
|
/// [`RoomMessageEventContent`]: super::room::message::RoomMessageEventContent
|
||||||
|
/// [`MessageType::Location`]: super::room::message::MessageType::Location
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, EventContent)]
|
#[derive(Clone, Debug, Serialize, Deserialize, EventContent)]
|
||||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||||
#[ruma_event(type = "m.location", kind = MessageLike)]
|
#[ruma_event(type = "m.location", kind = MessageLike)]
|
||||||
@ -55,6 +70,22 @@ impl LocationEventContent {
|
|||||||
pub fn with_message(message: MessageContent, location: LocationContent) -> Self {
|
pub fn with_message(message: MessageContent, location: LocationContent) -> Self {
|
||||||
Self { message, location, asset: Default::default(), ts: None, relates_to: None }
|
Self { message, location, asset: Default::default(), ts: None, relates_to: None }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new `LocationEventContent` from the given `LocationMessageEventContent` and
|
||||||
|
/// optional relation.
|
||||||
|
pub fn from_location_room_message(
|
||||||
|
content: LocationMessageEventContent,
|
||||||
|
relates_to: Option<Relation>,
|
||||||
|
) -> Self {
|
||||||
|
let LocationMessageEventContent { body, geo_uri, message, location, asset, ts, .. } =
|
||||||
|
content;
|
||||||
|
|
||||||
|
let message = message.unwrap_or_else(|| MessageContent::plain(body));
|
||||||
|
let location = location.unwrap_or_else(|| LocationContent::new(geo_uri));
|
||||||
|
let asset = asset.unwrap_or_default();
|
||||||
|
|
||||||
|
Self { message, location, asset, ts, relates_to }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Location content.
|
/// Location content.
|
||||||
|
@ -31,6 +31,11 @@ use crate::{
|
|||||||
serde::{JsonObject, StringEnum},
|
serde::{JsonObject, StringEnum},
|
||||||
DeviceId, EventId, MxcUri, PrivOwnedStr, UserId,
|
DeviceId, EventId, MxcUri, PrivOwnedStr, UserId,
|
||||||
};
|
};
|
||||||
|
#[cfg(feature = "unstable-msc3488")]
|
||||||
|
use crate::{
|
||||||
|
events::location::{AssetContent, LocationContent, LocationEventContent},
|
||||||
|
MilliSecondsSinceUnixEpoch,
|
||||||
|
};
|
||||||
|
|
||||||
mod content_serde;
|
mod content_serde;
|
||||||
pub mod feedback;
|
pub mod feedback;
|
||||||
@ -249,6 +254,20 @@ impl From<ImageEventContent> for RoomMessageEventContent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "unstable-msc3488")]
|
||||||
|
impl From<LocationEventContent> for RoomMessageEventContent {
|
||||||
|
fn from(content: LocationEventContent) -> Self {
|
||||||
|
let LocationEventContent { message, location, asset, ts, relates_to } = content;
|
||||||
|
|
||||||
|
Self {
|
||||||
|
msgtype: MessageType::Location(LocationMessageEventContent::from_extensible_content(
|
||||||
|
message, location, asset, ts,
|
||||||
|
)),
|
||||||
|
relates_to,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "unstable-msc1767")]
|
#[cfg(feature = "unstable-msc1767")]
|
||||||
impl From<MessageEventContent> for RoomMessageEventContent {
|
impl From<MessageEventContent> for RoomMessageEventContent {
|
||||||
fn from(content: MessageEventContent) -> Self {
|
fn from(content: MessageEventContent) -> Self {
|
||||||
@ -1073,6 +1092,10 @@ impl ImageMessageEventContent {
|
|||||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||||
#[serde(tag = "msgtype", rename = "m.location")]
|
#[serde(tag = "msgtype", rename = "m.location")]
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "unstable-msc3488",
|
||||||
|
serde(from = "content_serde::LocationMessageEventContentDeHelper")
|
||||||
|
)]
|
||||||
pub struct LocationMessageEventContent {
|
pub struct LocationMessageEventContent {
|
||||||
/// A description of the location e.g. "Big Ben, London, UK", or some kind of content
|
/// A description of the location e.g. "Big Ben, London, UK", or some kind of content
|
||||||
/// description for accessibility, e.g. "location attachment".
|
/// description for accessibility, e.g. "location attachment".
|
||||||
@ -1084,12 +1107,75 @@ pub struct LocationMessageEventContent {
|
|||||||
/// Info about the location being represented.
|
/// Info about the location being represented.
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub info: Option<Box<LocationInfo>>,
|
pub info: Option<Box<LocationInfo>>,
|
||||||
|
|
||||||
|
/// Extensible-event text representation of the message.
|
||||||
|
///
|
||||||
|
/// If present, this should be preferred over the `body` field.
|
||||||
|
#[cfg(feature = "unstable-msc3488")]
|
||||||
|
#[serde(flatten, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub message: Option<MessageContent>,
|
||||||
|
|
||||||
|
/// Extensible-event location info of the message.
|
||||||
|
///
|
||||||
|
/// If present, this should be preferred over the `geo_uri` field.
|
||||||
|
#[cfg(feature = "unstable-msc3488")]
|
||||||
|
#[serde(rename = "org.matrix.msc3488.location", skip_serializing_if = "Option::is_none")]
|
||||||
|
pub location: Option<LocationContent>,
|
||||||
|
|
||||||
|
/// Extensible-event asset this message refers to.
|
||||||
|
#[cfg(feature = "unstable-msc3488")]
|
||||||
|
#[serde(rename = "org.matrix.msc3488.asset", skip_serializing_if = "Option::is_none")]
|
||||||
|
pub asset: Option<AssetContent>,
|
||||||
|
|
||||||
|
/// Extensible-event timestamp this message refers to.
|
||||||
|
#[cfg(feature = "unstable-msc3488")]
|
||||||
|
#[serde(rename = "org.matrix.msc3488.ts", skip_serializing_if = "Option::is_none")]
|
||||||
|
pub ts: Option<MilliSecondsSinceUnixEpoch>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LocationMessageEventContent {
|
impl LocationMessageEventContent {
|
||||||
/// Creates a new `RoomLocationMessageEventContent` with the given body and geo URI.
|
/// Creates a new `RoomLocationMessageEventContent` with the given body and geo URI.
|
||||||
pub fn new(body: String, geo_uri: String) -> Self {
|
pub fn new(body: String, geo_uri: String) -> Self {
|
||||||
Self { body, geo_uri, info: None }
|
Self {
|
||||||
|
#[cfg(feature = "unstable-msc3488")]
|
||||||
|
message: Some(MessageContent::plain(body.clone())),
|
||||||
|
#[cfg(feature = "unstable-msc3488")]
|
||||||
|
location: Some(LocationContent::new(geo_uri.clone())),
|
||||||
|
#[cfg(feature = "unstable-msc3488")]
|
||||||
|
asset: None,
|
||||||
|
#[cfg(feature = "unstable-msc3488")]
|
||||||
|
ts: None,
|
||||||
|
body,
|
||||||
|
geo_uri,
|
||||||
|
info: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new `LocationMessageEventContent` with the given message, location info, asset and
|
||||||
|
/// timestamp.
|
||||||
|
#[cfg(feature = "unstable-msc3488")]
|
||||||
|
pub fn from_extensible_content(
|
||||||
|
message: MessageContent,
|
||||||
|
location: LocationContent,
|
||||||
|
asset: AssetContent,
|
||||||
|
ts: Option<MilliSecondsSinceUnixEpoch>,
|
||||||
|
) -> Self {
|
||||||
|
let body = if let Some(body) = message.find_plain() {
|
||||||
|
body.to_owned()
|
||||||
|
} else {
|
||||||
|
message[0].body.clone()
|
||||||
|
};
|
||||||
|
let geo_uri = location.uri.clone();
|
||||||
|
|
||||||
|
Self {
|
||||||
|
message: Some(message),
|
||||||
|
location: Some(location),
|
||||||
|
asset: Some(asset),
|
||||||
|
ts,
|
||||||
|
body,
|
||||||
|
geo_uri,
|
||||||
|
info: None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,11 @@ use serde_json::value::RawValue as RawJsonValue;
|
|||||||
|
|
||||||
#[cfg(feature = "unstable-msc3245")]
|
#[cfg(feature = "unstable-msc3245")]
|
||||||
use super::VoiceContent;
|
use super::VoiceContent;
|
||||||
|
#[cfg(feature = "unstable-msc3488")]
|
||||||
|
use super::{
|
||||||
|
AssetContent, LocationContent, LocationInfo, LocationMessageEventContent,
|
||||||
|
MilliSecondsSinceUnixEpoch,
|
||||||
|
};
|
||||||
#[cfg(feature = "unstable-msc3246")]
|
#[cfg(feature = "unstable-msc3246")]
|
||||||
use super::{AudioContent, AudioInfo, AudioMessageEventContent};
|
use super::{AudioContent, AudioInfo, AudioMessageEventContent};
|
||||||
#[cfg(feature = "unstable-msc3551")]
|
#[cfg(feature = "unstable-msc3551")]
|
||||||
@ -278,6 +283,77 @@ impl From<ImageMessageEventContentDeHelper> for ImageMessageEventContent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Helper struct for deserializing `LocationMessageEventContent` with stable and unstable field
|
||||||
|
/// names.
|
||||||
|
///
|
||||||
|
/// It's not possible to use the `alias` attribute of serde because of
|
||||||
|
/// https://github.com/serde-rs/serde/issues/1504.
|
||||||
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
|
#[cfg(feature = "unstable-msc3488")]
|
||||||
|
pub struct LocationMessageEventContentDeHelper {
|
||||||
|
/// A description of the location.
|
||||||
|
pub body: String,
|
||||||
|
|
||||||
|
/// A geo URI representing the location.
|
||||||
|
pub geo_uri: String,
|
||||||
|
|
||||||
|
/// Info about the location being represented.
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub info: Option<Box<LocationInfo>>,
|
||||||
|
|
||||||
|
/// Extensible-event text representation of the message.
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub message: Option<MessageContent>,
|
||||||
|
|
||||||
|
/// Extensible-event location info of the message, with stable name.
|
||||||
|
#[serde(rename = "m.location")]
|
||||||
|
pub location_stable: Option<LocationContent>,
|
||||||
|
|
||||||
|
/// Extensible-event location info of the message, with unstable name.
|
||||||
|
#[serde(rename = "org.matrix.msc3488.location")]
|
||||||
|
pub location_unstable: Option<LocationContent>,
|
||||||
|
|
||||||
|
/// Extensible-event asset this message refers to, with stable name.
|
||||||
|
#[serde(rename = "m.asset")]
|
||||||
|
pub asset_stable: Option<AssetContent>,
|
||||||
|
|
||||||
|
/// Extensible-event asset this message refers to, with unstable name.
|
||||||
|
#[serde(rename = "org.matrix.msc3488.asset")]
|
||||||
|
pub asset_unstable: Option<AssetContent>,
|
||||||
|
|
||||||
|
/// Extensible-event timestamp this message refers to, with stable name.
|
||||||
|
#[serde(rename = "m.ts")]
|
||||||
|
pub ts_stable: Option<MilliSecondsSinceUnixEpoch>,
|
||||||
|
|
||||||
|
/// Extensible-event timestamp this message refers to, with unstable name.
|
||||||
|
#[serde(rename = "org.matrix.msc3488.ts")]
|
||||||
|
pub ts_unstable: Option<MilliSecondsSinceUnixEpoch>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "unstable-msc3488")]
|
||||||
|
impl From<LocationMessageEventContentDeHelper> for LocationMessageEventContent {
|
||||||
|
fn from(helper: LocationMessageEventContentDeHelper) -> Self {
|
||||||
|
let LocationMessageEventContentDeHelper {
|
||||||
|
body,
|
||||||
|
geo_uri,
|
||||||
|
info,
|
||||||
|
message,
|
||||||
|
location_stable,
|
||||||
|
location_unstable,
|
||||||
|
asset_stable,
|
||||||
|
asset_unstable,
|
||||||
|
ts_stable,
|
||||||
|
ts_unstable,
|
||||||
|
} = helper;
|
||||||
|
|
||||||
|
let location = location_stable.or(location_unstable);
|
||||||
|
let asset = asset_stable.or(asset_unstable);
|
||||||
|
let ts = ts_stable.or(ts_unstable);
|
||||||
|
|
||||||
|
Self { body, geo_uri, info, message, location, asset, ts }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Helper struct for deserializing `VideoMessageEventContent` with stable and unstable field names.
|
/// Helper struct for deserializing `VideoMessageEventContent` with stable and unstable field names.
|
||||||
///
|
///
|
||||||
/// It's not possible to use the `alias` attribute of serde because of
|
/// It's not possible to use the `alias` attribute of serde because of
|
||||||
|
@ -11,7 +11,9 @@ use ruma_common::{
|
|||||||
ZoomLevelError,
|
ZoomLevelError,
|
||||||
},
|
},
|
||||||
message::MessageContent,
|
message::MessageContent,
|
||||||
room::message::{InReplyTo, Relation},
|
room::message::{
|
||||||
|
InReplyTo, LocationMessageEventContent, MessageType, Relation, RoomMessageEventContent,
|
||||||
|
},
|
||||||
AnyMessageLikeEvent, MessageLikeEvent, MessageLikeUnsigned,
|
AnyMessageLikeEvent, MessageLikeEvent, MessageLikeUnsigned,
|
||||||
},
|
},
|
||||||
room_id, user_id, MilliSecondsSinceUnixEpoch,
|
room_id, user_id, MilliSecondsSinceUnixEpoch,
|
||||||
@ -105,7 +107,7 @@ fn event_serialization() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn plain_content_deserialization() {
|
fn plain_content_deserialization() {
|
||||||
let json_data = json!({
|
let json_data = json!({
|
||||||
"org.matrix.msc1767.text": "Alice was at geo:51.5008,0.1247;u=35",
|
"m.text": "Alice was at geo:51.5008,0.1247;u=35",
|
||||||
"m.location": {
|
"m.location": {
|
||||||
"uri": "geo:51.5008,0.1247;u=35",
|
"uri": "geo:51.5008,0.1247;u=35",
|
||||||
},
|
},
|
||||||
@ -228,3 +230,73 @@ fn message_event_deserialization() {
|
|||||||
&& unsigned.is_empty()
|
&& unsigned.is_empty()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn room_message_serialization() {
|
||||||
|
let message_event_content =
|
||||||
|
RoomMessageEventContent::new(MessageType::Location(LocationMessageEventContent::new(
|
||||||
|
"Alice was at geo:51.5008,0.1247;u=35".to_owned(),
|
||||||
|
"geo:51.5008,0.1247;u=35".to_owned(),
|
||||||
|
)));
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
to_json_value(&message_event_content).unwrap(),
|
||||||
|
json!({
|
||||||
|
"body": "Alice was at geo:51.5008,0.1247;u=35",
|
||||||
|
"geo_uri": "geo:51.5008,0.1247;u=35",
|
||||||
|
"msgtype": "m.location",
|
||||||
|
"org.matrix.msc1767.text": "Alice was at geo:51.5008,0.1247;u=35",
|
||||||
|
"org.matrix.msc3488.location": {
|
||||||
|
"uri": "geo:51.5008,0.1247;u=35",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn room_message_stable_deserialization() {
|
||||||
|
let json_data = json!({
|
||||||
|
"body": "Alice was at geo:51.5008,0.1247;u=35",
|
||||||
|
"geo_uri": "geo:51.5008,0.1247;u=35",
|
||||||
|
"msgtype": "m.location",
|
||||||
|
"m.text": "Alice was at geo:51.5008,0.1247;u=35",
|
||||||
|
"m.location": {
|
||||||
|
"uri": "geo:51.5008,0.1247;u=35",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
let event_content = from_json_value::<RoomMessageEventContent>(json_data).unwrap();
|
||||||
|
assert_matches!(event_content.msgtype, MessageType::Location(_));
|
||||||
|
if let MessageType::Location(content) = event_content.msgtype {
|
||||||
|
assert_eq!(content.body, "Alice was at geo:51.5008,0.1247;u=35");
|
||||||
|
assert_eq!(content.geo_uri, "geo:51.5008,0.1247;u=35");
|
||||||
|
let message = content.message.unwrap();
|
||||||
|
assert_eq!(message.len(), 1);
|
||||||
|
assert_eq!(message[0].body, "Alice was at geo:51.5008,0.1247;u=35");
|
||||||
|
assert_eq!(content.location.unwrap().uri, "geo:51.5008,0.1247;u=35");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn room_message_unstable_deserialization() {
|
||||||
|
let json_data = json!({
|
||||||
|
"body": "Alice was at geo:51.5008,0.1247;u=35",
|
||||||
|
"geo_uri": "geo:51.5008,0.1247;u=35",
|
||||||
|
"msgtype": "m.location",
|
||||||
|
"org.matrix.msc1767.text": "Alice was at geo:51.5008,0.1247;u=35",
|
||||||
|
"org.matrix.msc3488.location": {
|
||||||
|
"uri": "geo:51.5008,0.1247;u=35",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
let event_content = from_json_value::<RoomMessageEventContent>(json_data).unwrap();
|
||||||
|
assert_matches!(event_content.msgtype, MessageType::Location(_));
|
||||||
|
if let MessageType::Location(content) = event_content.msgtype {
|
||||||
|
assert_eq!(content.body, "Alice was at geo:51.5008,0.1247;u=35");
|
||||||
|
assert_eq!(content.geo_uri, "geo:51.5008,0.1247;u=35");
|
||||||
|
let message = content.message.unwrap();
|
||||||
|
assert_eq!(message.len(), 1);
|
||||||
|
assert_eq!(message[0].body, "Alice was at geo:51.5008,0.1247;u=35");
|
||||||
|
assert_eq!(content.location.unwrap().uri, "geo:51.5008,0.1247;u=35");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user