1- use std:: { collections:: HashMap , pin:: pin, sync:: Arc } ;
1+ use std:: { collections:: HashMap , fs , path :: PathBuf , pin:: pin, sync:: Arc } ;
22
33use anyhow:: { Context , Result } ;
44use futures_util:: { pin_mut, StreamExt } ;
@@ -9,7 +9,8 @@ use matrix_sdk::{
99 TryFromReportedContentScoreError ,
1010 } ,
1111 send_queue:: RoomSendQueueUpdate as SdkRoomSendQueueUpdate ,
12- ComposerDraft as SdkComposerDraft , ComposerDraftType as SdkComposerDraftType , EncryptionState ,
12+ ComposerDraft as SdkComposerDraft , ComposerDraftType as SdkComposerDraftType ,
13+ DraftAttachment as SdkDraftAttachment , DraftAttachmentContent , DraftThumbnail , EncryptionState ,
1314 PredecessorRoom as SdkPredecessorRoom , RoomHero as SdkRoomHero , RoomMemberships , RoomState ,
1415 SuccessorRoom as SdkSuccessorRoom ,
1516} ;
@@ -45,11 +46,14 @@ use crate::{
4546 live_location_share:: { LastLocation , LiveLocationShare } ,
4647 room_member:: { RoomMember , RoomMemberWithSenderInfo } ,
4748 room_preview:: RoomPreview ,
48- ruma:: { ImageInfo , LocationContent , MediaSource } ,
49+ ruma:: {
50+ AudioInfo , FileInfo , ImageInfo , LocationContent , MediaSource , ThumbnailInfo , VideoInfo ,
51+ } ,
4952 runtime:: get_runtime_handle,
5053 timeline:: {
5154 configuration:: { TimelineConfiguration , TimelineFilter } ,
5255 AbstractProgress , EventTimelineItem , LatestEventValue , ReceiptType , SendHandle , Timeline ,
56+ UploadSource ,
5357 } ,
5458 utils:: { u64_to_uint, AsyncRuntimeDropped } ,
5559 TaskHandle ,
@@ -1442,21 +1446,257 @@ pub struct ComposerDraft {
14421446 pub html_text : Option < String > ,
14431447 /// The type of draft.
14441448 pub draft_type : ComposerDraftType ,
1449+ /// Attachments associated with this draft.
1450+ pub attachments : Vec < DraftAttachment > ,
14451451}
14461452
14471453impl From < SdkComposerDraft > for ComposerDraft {
14481454 fn from ( value : SdkComposerDraft ) -> Self {
1449- let SdkComposerDraft { plain_text, html_text, draft_type } = value;
1450- Self { plain_text, html_text, draft_type : draft_type. into ( ) }
1455+ let SdkComposerDraft { plain_text, html_text, draft_type, attachments } = value;
1456+ Self {
1457+ plain_text,
1458+ html_text,
1459+ draft_type : draft_type. into ( ) ,
1460+ attachments : attachments. into_iter ( ) . map ( |a| a. into ( ) ) . collect ( ) ,
1461+ }
14511462 }
14521463}
14531464
14541465impl TryFrom < ComposerDraft > for SdkComposerDraft {
1455- type Error = ruma :: IdParseError ;
1466+ type Error = ClientError ;
14561467
14571468 fn try_from ( value : ComposerDraft ) -> std:: result:: Result < Self , Self :: Error > {
1458- let ComposerDraft { plain_text, html_text, draft_type } = value;
1459- Ok ( Self { plain_text, html_text, draft_type : draft_type. try_into ( ) ? } )
1469+ let ComposerDraft { plain_text, html_text, draft_type, attachments } = value;
1470+ Ok ( Self {
1471+ plain_text,
1472+ html_text,
1473+ draft_type : draft_type. try_into ( ) ?,
1474+ attachments : attachments
1475+ . into_iter ( )
1476+ . map ( |a| a. try_into ( ) )
1477+ . collect :: < std:: result:: Result < Vec < _ > , _ > > ( ) ?,
1478+ } )
1479+ }
1480+ }
1481+
1482+ /// An attachment stored with a composer draft.
1483+ #[ derive( uniffi:: Enum ) ]
1484+ pub enum DraftAttachment {
1485+ Audio { audio_info : AudioInfo , source : UploadSource } ,
1486+ File { file_info : FileInfo , source : UploadSource } ,
1487+ Image { image_info : ImageInfo , source : UploadSource , thumbnail_source : Option < UploadSource > } ,
1488+ Video { video_info : VideoInfo , source : UploadSource , thumbnail_source : Option < UploadSource > } ,
1489+ }
1490+
1491+ impl From < SdkDraftAttachment > for DraftAttachment {
1492+ fn from ( value : SdkDraftAttachment ) -> Self {
1493+ match value. content {
1494+ DraftAttachmentContent :: Image {
1495+ data,
1496+ mimetype,
1497+ size,
1498+ width,
1499+ height,
1500+ blurhash,
1501+ thumbnail,
1502+ } => {
1503+ let thumbnail_source = thumbnail. as_ref ( ) . map ( |t| UploadSource :: Data {
1504+ bytes : t. data . clone ( ) ,
1505+ filename : t. filename . clone ( ) ,
1506+ } ) ;
1507+ let thumbnail_info = thumbnail. map ( |t| ThumbnailInfo {
1508+ width : t. width ,
1509+ height : t. height ,
1510+ mimetype : t. mimetype ,
1511+ size : t. size ,
1512+ } ) ;
1513+ DraftAttachment :: Image {
1514+ image_info : ImageInfo {
1515+ height,
1516+ width,
1517+ mimetype,
1518+ size,
1519+ thumbnail_info,
1520+ thumbnail_source : None ,
1521+ blurhash,
1522+ is_animated : None ,
1523+ } ,
1524+ source : UploadSource :: Data { bytes : data, filename : value. filename } ,
1525+ thumbnail_source,
1526+ }
1527+ }
1528+ DraftAttachmentContent :: Video {
1529+ data,
1530+ mimetype,
1531+ size,
1532+ width,
1533+ height,
1534+ duration,
1535+ blurhash,
1536+ thumbnail,
1537+ } => {
1538+ let thumbnail_source = thumbnail. as_ref ( ) . map ( |t| UploadSource :: Data {
1539+ bytes : t. data . clone ( ) ,
1540+ filename : t. filename . clone ( ) ,
1541+ } ) ;
1542+ let thumbnail_info = thumbnail. map ( |t| ThumbnailInfo {
1543+ width : t. width ,
1544+ height : t. height ,
1545+ mimetype : t. mimetype ,
1546+ size : t. size ,
1547+ } ) ;
1548+ DraftAttachment :: Video {
1549+ video_info : VideoInfo {
1550+ duration,
1551+ height,
1552+ width,
1553+ mimetype,
1554+ size,
1555+ thumbnail_info,
1556+ thumbnail_source : None ,
1557+ blurhash,
1558+ } ,
1559+ source : UploadSource :: Data { bytes : data, filename : value. filename } ,
1560+ thumbnail_source,
1561+ }
1562+ }
1563+ DraftAttachmentContent :: Audio { data, mimetype, size, duration } => {
1564+ DraftAttachment :: Audio {
1565+ audio_info : AudioInfo { duration, size, mimetype } ,
1566+ source : UploadSource :: Data { bytes : data, filename : value. filename } ,
1567+ }
1568+ }
1569+ DraftAttachmentContent :: File { data, mimetype, size } => DraftAttachment :: File {
1570+ file_info : FileInfo {
1571+ mimetype,
1572+ size,
1573+ thumbnail_info : None ,
1574+ thumbnail_source : None ,
1575+ } ,
1576+ source : UploadSource :: Data { bytes : data, filename : value. filename } ,
1577+ } ,
1578+ }
1579+ }
1580+ }
1581+
1582+ /// Resolve the bytes and filename from an `UploadSource`, reading the file
1583+ /// contents if needed.
1584+ fn read_upload_source ( source : UploadSource ) -> Result < ( Vec < u8 > , String ) , ClientError > {
1585+ match source {
1586+ UploadSource :: Data { bytes, filename } => Ok ( ( bytes, filename) ) ,
1587+ UploadSource :: File { filename } => {
1588+ let path: PathBuf = filename. into ( ) ;
1589+ let filename = path
1590+ . file_name ( )
1591+ . ok_or ( ClientError :: Generic {
1592+ msg : "Invalid attachment path" . to_owned ( ) ,
1593+ details : None ,
1594+ } ) ?
1595+ . to_str ( )
1596+ . ok_or ( ClientError :: Generic {
1597+ msg : "Invalid attachment path" . to_owned ( ) ,
1598+ details : None ,
1599+ } ) ?
1600+ . to_owned ( ) ;
1601+
1602+ let bytes = fs:: read ( & path) . map_err ( |_| ClientError :: Generic {
1603+ msg : "Could not load file" . to_owned ( ) ,
1604+ details : None ,
1605+ } ) ?;
1606+
1607+ Ok ( ( bytes, filename) )
1608+ }
1609+ }
1610+ }
1611+
1612+ impl TryFrom < DraftAttachment > for SdkDraftAttachment {
1613+ type Error = ClientError ;
1614+
1615+ fn try_from ( value : DraftAttachment ) -> Result < Self , Self :: Error > {
1616+ match value {
1617+ DraftAttachment :: Image { image_info, source, thumbnail_source, .. } => {
1618+ let ( data, filename) = read_upload_source ( source) ?;
1619+ let thumbnail = match ( image_info. thumbnail_info , thumbnail_source) {
1620+ ( Some ( info) , Some ( source) ) => {
1621+ let ( data, filename) = read_upload_source ( source) ?;
1622+ Some ( DraftThumbnail {
1623+ filename,
1624+ data,
1625+ mimetype : info. mimetype ,
1626+ width : info. width ,
1627+ height : info. height ,
1628+ size : info. size ,
1629+ } )
1630+ }
1631+ _ => None ,
1632+ } ;
1633+ Ok ( Self {
1634+ filename,
1635+ content : DraftAttachmentContent :: Image {
1636+ data,
1637+ mimetype : image_info. mimetype ,
1638+ size : image_info. size ,
1639+ width : image_info. width ,
1640+ height : image_info. height ,
1641+ blurhash : image_info. blurhash ,
1642+ thumbnail,
1643+ } ,
1644+ } )
1645+ }
1646+ DraftAttachment :: Video { video_info, source, thumbnail_source, .. } => {
1647+ let ( data, filename) = read_upload_source ( source) ?;
1648+ let thumbnail = match ( video_info. thumbnail_info , thumbnail_source) {
1649+ ( Some ( info) , Some ( source) ) => {
1650+ let ( data, filename) = read_upload_source ( source) ?;
1651+ Some ( DraftThumbnail {
1652+ filename,
1653+ data,
1654+ mimetype : info. mimetype ,
1655+ width : info. width ,
1656+ height : info. height ,
1657+ size : info. size ,
1658+ } )
1659+ }
1660+ _ => None ,
1661+ } ;
1662+ Ok ( Self {
1663+ filename,
1664+ content : DraftAttachmentContent :: Video {
1665+ data,
1666+ mimetype : video_info. mimetype ,
1667+ size : video_info. size ,
1668+ width : video_info. width ,
1669+ height : video_info. height ,
1670+ duration : video_info. duration ,
1671+ blurhash : video_info. blurhash ,
1672+ thumbnail,
1673+ } ,
1674+ } )
1675+ }
1676+ DraftAttachment :: Audio { audio_info, source, .. } => {
1677+ let ( data, filename) = read_upload_source ( source) ?;
1678+ Ok ( Self {
1679+ filename,
1680+ content : DraftAttachmentContent :: Audio {
1681+ data,
1682+ mimetype : audio_info. mimetype ,
1683+ size : audio_info. size ,
1684+ duration : audio_info. duration ,
1685+ } ,
1686+ } )
1687+ }
1688+ DraftAttachment :: File { file_info, source, .. } => {
1689+ let ( data, filename) = read_upload_source ( source) ?;
1690+ Ok ( Self {
1691+ filename,
1692+ content : DraftAttachmentContent :: File {
1693+ data,
1694+ mimetype : file_info. mimetype ,
1695+ size : file_info. size ,
1696+ } ,
1697+ } )
1698+ }
1699+ }
14601700 }
14611701}
14621702
0 commit comments