Skip to content

Commit

Permalink
sdk: basic support for sending and stopping live location shares
Browse files Browse the repository at this point in the history
  • Loading branch information
torrybr committed Jul 25, 2024
1 parent 925c5b2 commit 33f58dd
Show file tree
Hide file tree
Showing 6 changed files with 347 additions and 4 deletions.
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ ruma = { git = "https://github.com/matrix-org/ruma", rev = "f25b3220d0c3ece77200
"compat-encrypted-stickers",
"unstable-msc3401",
"unstable-msc3266",
"unstable-msc3488",
"unstable-msc3489",
"unstable-msc4075",
"unstable-msc4140",
] }
Expand Down
8 changes: 8 additions & 0 deletions crates/matrix-sdk-base/src/rooms/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ pub use normal::{
use ruma::{
assign,
events::{
beacon_info::BeaconInfoEventContent,
call::member::CallMemberEventContent,
macros::EventContent,
room::{
Expand Down Expand Up @@ -81,6 +82,9 @@ impl fmt::Display for DisplayName {
pub struct BaseRoomInfo {
/// The avatar URL of this room.
pub(crate) avatar: Option<MinimalStateEvent<RoomAvatarEventContent>>,
/// All shared live location beacons of this room.
#[serde(skip_serializing_if = "BTreeMap::is_empty", default)]
pub(crate) beacons: BTreeMap<OwnedUserId, MinimalStateEvent<BeaconInfoEventContent>>,
/// The canonical alias of this room.
pub(crate) canonical_alias: Option<MinimalStateEvent<RoomCanonicalAliasEventContent>>,
/// The `m.room.create` event content of this room.
Expand Down Expand Up @@ -141,6 +145,9 @@ impl BaseRoomInfo {
/// Returns true if the event modified the info, false otherwise.
pub fn handle_state_event(&mut self, ev: &AnySyncStateEvent) -> bool {
match ev {
AnySyncStateEvent::BeaconInfo(b) => {
self.beacons.insert(b.state_key().clone(), b.into());
}
// No redacted branch - enabling encryption cannot be undone.
AnySyncStateEvent::RoomEncryption(SyncStateEvent::Original(encryption)) => {
self.encryption = Some(encryption.content.clone());
Expand Down Expand Up @@ -335,6 +342,7 @@ impl Default for BaseRoomInfo {
fn default() -> Self {
Self {
avatar: None,
beacons: BTreeMap::new(),
canonical_alias: None,
create: None,
dm_targets: Default::default(),
Expand Down
1 change: 1 addition & 0 deletions crates/matrix-sdk-base/src/store/migration_helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ impl BaseRoomInfoV1 {

Box::new(BaseRoomInfo {
avatar,
beacons: BTreeMap::new(),
canonical_alias,
create,
dm_targets,
Expand Down
33 changes: 33 additions & 0 deletions crates/matrix-sdk/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,39 @@ pub enum ImageError {
ThumbnailBiggerThanOriginal,
}

/// Errors that can happen when interacting with the beacon API.
#[derive(Debug, Error)]
pub enum BeaconError {
// A network error occurred.
#[error("Network error: {0}")]
Network(#[from] HttpError),

// The beacon information is not found.
#[error("Existing beacon information not found.")]
NotFound,

// The redacted event is not an error, but it's not useful for the client.
#[error("Beacon event is redacted and cannot be processed.")]
Redacted,

// The client must join the room to access the beacon information.
#[error("Must join the room to access beacon information.")]
Stripped,

#[error("Deserialization error: {0}")]
Deserialization(#[from] serde_json::Error),

// Allow for other errors to be wrapped.
#[error("Other error: {0}")]
Other(Box<Error>),
}

impl From<Error> for BeaconError {
fn from(err: Error) -> Self {
BeaconError::Other(Box::new(err))
}
}

/// Errors that can happen when refreshing an access token.
///
/// This is usually only returned by [`Client::refresh_access_token()`], unless
Expand Down
66 changes: 65 additions & 1 deletion crates/matrix-sdk/src/room/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ use ruma::{
},
assign,
events::{
beacon_info::BeaconInfoEventContent,
call::notify::{ApplicationType, CallNotifyEventContent, NotifyType},
direct::DirectEventContent,
marked_unread::MarkedUnreadEventContent,
Expand Down Expand Up @@ -97,7 +98,7 @@ use crate::{
attachment::AttachmentConfig,
client::WeakClient,
config::RequestConfig,
error::WrongRoomState,
error::{BeaconError, WrongRoomState},
event_cache::{self, EventCacheDropHandles, RoomEventCache},
event_handler::{EventHandler, EventHandlerDropGuard, EventHandlerHandle, SyncEvent},
media::{MediaFormat, MediaRequest},
Expand Down Expand Up @@ -2668,6 +2669,69 @@ impl Room {
Ok(())
}

/// Start sharing live location in the room.
///
/// # Arguments
///
/// * `duration_millis` - The duration for which the live location is
/// shared, in milliseconds.
/// * `description` - An optional description for the live location share.
///
/// # Errors
///
/// Returns an error if the room is not joined or if the state event could
/// not be sent.
pub async fn start_live_location_share(
&self,
duration_millis: u64,
description: Option<String>,
) -> Result<send_state_event::v3::Response> {
self.ensure_room_joined()?;

self.send_state_event_for_key(
self.own_user_id(),
BeaconInfoEventContent::new(
description,
Duration::from_millis(duration_millis),
true,
None,
),
)
.await
}

/// Stop sharing live location in the room.
///
/// # Errors
///
/// Returns an error if the room is not joined, if the beacon information
/// is redacted or stripped, or if the state event is not found.
pub async fn stop_live_location_share(
&self,
) -> Result<send_state_event::v3::Response, BeaconError> {
self.ensure_room_joined()?;

if let Some(raw_event) = self
.get_state_event_static_for_key::<BeaconInfoEventContent, _>(self.own_user_id())
.await?
{
match raw_event.deserialize() {
Ok(SyncOrStrippedState::Sync(SyncStateEvent::Original(beacon_info))) => {
let mut content = beacon_info.content.clone();
content.stop();
Ok(self.send_state_event_for_key(self.own_user_id(), content).await?)
}
Ok(SyncOrStrippedState::Sync(SyncStateEvent::Redacted(_))) => {
Err(BeaconError::Redacted)
}
Ok(SyncOrStrippedState::Stripped(_)) => Err(BeaconError::Stripped),
Err(e) => Err(BeaconError::Deserialization(e)),
}
} else {
Err(BeaconError::NotFound)
}
}

/// Send a call notification event in the current room.
///
/// This is only supposed to be used in **custom** situations where the user
Expand Down
Loading

0 comments on commit 33f58dd

Please sign in to comment.