diff --git a/Cargo.lock b/Cargo.lock index 4e64fd0686c..ea7df20c86f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2795,6 +2795,7 @@ dependencies = [ "tracing-subscriber", "uniffi", "url", + "uuid", "vergen", "zeroize", ] diff --git a/bindings/matrix-sdk-ffi/Cargo.toml b/bindings/matrix-sdk-ffi/Cargo.toml index 62a9e4eab69..996406b614e 100644 --- a/bindings/matrix-sdk-ffi/Cargo.toml +++ b/bindings/matrix-sdk-ffi/Cargo.toml @@ -50,6 +50,7 @@ tokio-stream = "0.1.8" uniffi = { workspace = true, features = ["tokio"] } url = "2.2.2" zeroize = { workspace = true } +uuid = { version = "1.4.1", features = ["v4"] } [target.'cfg(target_os = "android")'.dependencies] log-panics = { version = "2", features = ["with-backtrace"] } diff --git a/bindings/matrix-sdk-ffi/src/room.rs b/bindings/matrix-sdk-ffi/src/room.rs index f833aaa5da8..9811e834114 100644 --- a/bindings/matrix-sdk-ffi/src/room.rs +++ b/bindings/matrix-sdk-ffi/src/room.rs @@ -12,12 +12,17 @@ use matrix_sdk::{ api::client::{receipt::create_receipt::v3::ReceiptType, room::report_content}, events::{ location::{AssetType as RumaAssetType, LocationContent, ZoomLevel}, + poll::unstable_start::{ + UnstablePollAnswer, UnstablePollAnswers, UnstablePollStartContentBlock, + UnstablePollStartEventContent, + }, receipt::ReceiptThread, relation::{Annotation, Replacement}, room::message::{ ForwardThread, LocationMessageEventContent, MessageType, Relation, RoomMessageEvent, RoomMessageEventContent, }, + AnyMessageLikeEventContent, }, EventId, UserId, }, @@ -30,6 +35,7 @@ use tokio::{ task::{AbortHandle, JoinHandle}, }; use tracing::{error, info}; +use uuid::Uuid; use super::RUNTIME; use crate::{ @@ -37,7 +43,7 @@ use crate::{ error::{ClientError, RoomError}, room_member::{MessageLikeEventType, RoomMember, StateEventType}, timeline::{ - AudioInfo, FileInfo, ImageInfo, ThumbnailInfo, TimelineDiff, TimelineItem, + AudioInfo, FileInfo, ImageInfo, PollKind, ThumbnailInfo, TimelineDiff, TimelineItem, TimelineListener, VideoInfo, }, TaskHandle, @@ -340,6 +346,50 @@ impl Room { }); } + pub fn create_poll( + &self, + question: String, + answers: Vec, + max_selections: u8, + poll_kind: PollKind, + txn_id: Option, + ) -> Result<(), ClientError> { + let timeline = match &*RUNTIME.block_on(self.timeline.read()) { + Some(t) => Arc::clone(t), + None => { + return Err(anyhow!("Timeline not set up, can't send the poll").into()); + } + }; + + let poll_answers_vec: Vec = answers + .iter() + .map(|answer| UnstablePollAnswer::new(Uuid::new_v4().to_string(), answer)) + .collect(); + + let poll_answers = UnstablePollAnswers::try_from(poll_answers_vec) + .context("Failed to create poll answers")?; + + let mut poll_content_block = + UnstablePollStartContentBlock::new(question.clone(), poll_answers); + poll_content_block.kind = poll_kind.into(); + poll_content_block.max_selections = max_selections.into(); + + let fallback_text = answers + .iter() + .enumerate() + .fold(question, |acc, (index, answer)| format!("{acc}\n{}. {answer}", index + 1)); + + let poll_start_event_content = + UnstablePollStartEventContent::plain_text(fallback_text, poll_content_block); + let event_content = AnyMessageLikeEventContent::UnstablePollStart(poll_start_event_content); + + RUNTIME.spawn(async move { + timeline.send(event_content, txn_id.as_deref().map(Into::into)).await; + }); + + Ok(()) + } + pub fn send_reply( &self, msg: String, diff --git a/bindings/matrix-sdk-ffi/src/timeline.rs b/bindings/matrix-sdk-ffi/src/timeline.rs index 7103adfdf85..5dbf3560aec 100644 --- a/bindings/matrix-sdk-ffi/src/timeline.rs +++ b/bindings/matrix-sdk-ffi/src/timeline.rs @@ -7,6 +7,7 @@ use matrix_sdk::{ attachment::{BaseAudioInfo, BaseFileInfo, BaseImageInfo, BaseThumbnailInfo, BaseVideoInfo}, ruma::events::{ location::AssetType as RumaAssetType, + poll::start::PollKind as RumaPollKind, room::{ message::{ AudioInfo as RumaAudioInfo, @@ -1270,6 +1271,15 @@ pub enum PollKind { Undisclosed, } +impl From for RumaPollKind { + fn from(value: PollKind) -> Self { + match value { + PollKind::Disclosed => Self::Disclosed, + PollKind::Undisclosed => Self::Undisclosed, + } + } +} + #[derive(Clone, uniffi::Record)] pub struct PollAnswer { pub id: String, diff --git a/crates/matrix-sdk-ui/Cargo.toml b/crates/matrix-sdk-ui/Cargo.toml index 058966ff44b..5ad510f04f1 100644 --- a/crates/matrix-sdk-ui/Cargo.toml +++ b/crates/matrix-sdk-ui/Cargo.toml @@ -34,7 +34,7 @@ matrix-sdk-crypto = { version = "0.6.0", path = "../matrix-sdk-crypto" } mime = "0.3.16" once_cell = { workspace = true } pin-project-lite = "0.2.9" -ruma = { workspace = true, features = ["unstable-sanitize"] } +ruma = { workspace = true, features = ["unstable-sanitize", "unstable-msc3381"] } serde = { workspace = true } serde_json = { workspace = true } thiserror = { workspace = true }