@@ -19,8 +19,8 @@ use xmtp_mls::groups::device_sync::preference_sync::UserPreferenceUpdate;
19
19
use xmtp_mls:: groups:: scoped_client:: LocalScopedGroupClient ;
20
20
use xmtp_mls:: groups:: HmacKey ;
21
21
use xmtp_mls:: storage:: group:: ConversationType ;
22
- use xmtp_mls:: storage:: group_message:: MsgQueryArgs ;
23
22
use xmtp_mls:: storage:: group_message:: SortDirection ;
23
+ use xmtp_mls:: storage:: group_message:: { ContentType , MsgQueryArgs } ;
24
24
use xmtp_mls:: {
25
25
api:: ApiClientWrapper ,
26
26
builder:: ClientBuilder ,
@@ -1259,6 +1259,38 @@ pub struct FfiListMessagesOptions {
1259
1259
pub limit : Option < i64 > ,
1260
1260
pub delivery_status : Option < FfiDeliveryStatus > ,
1261
1261
pub direction : Option < FfiDirection > ,
1262
+ pub content_types : Option < Vec < FfiContentType > > ,
1263
+ }
1264
+
1265
+ #[ derive( uniffi:: Enum , Clone ) ]
1266
+ pub enum FfiContentType {
1267
+ Unknown ,
1268
+ Text ,
1269
+ GroupMembershipChange ,
1270
+ GroupUpdated ,
1271
+ Reaction ,
1272
+ ReadReceipt ,
1273
+ Reply ,
1274
+ Attachment ,
1275
+ RemoteAttachment ,
1276
+ TransactionReference ,
1277
+ }
1278
+
1279
+ impl From < FfiContentType > for ContentType {
1280
+ fn from ( value : FfiContentType ) -> Self {
1281
+ match value {
1282
+ FfiContentType :: Unknown => ContentType :: Unknown ,
1283
+ FfiContentType :: Text => ContentType :: Text ,
1284
+ FfiContentType :: GroupMembershipChange => ContentType :: GroupMembershipChange ,
1285
+ FfiContentType :: GroupUpdated => ContentType :: GroupUpdated ,
1286
+ FfiContentType :: Reaction => ContentType :: Reaction ,
1287
+ FfiContentType :: ReadReceipt => ContentType :: ReadReceipt ,
1288
+ FfiContentType :: Reply => ContentType :: Reply ,
1289
+ FfiContentType :: Attachment => ContentType :: Attachment ,
1290
+ FfiContentType :: RemoteAttachment => ContentType :: RemoteAttachment ,
1291
+ FfiContentType :: TransactionReference => ContentType :: TransactionReference ,
1292
+ }
1293
+ }
1262
1294
}
1263
1295
1264
1296
#[ derive( uniffi:: Record , Clone , Default ) ]
@@ -1331,7 +1363,11 @@ impl FfiConversation {
1331
1363
. maybe_kind ( kind)
1332
1364
. maybe_delivery_status ( delivery_status)
1333
1365
. maybe_limit ( opts. limit )
1334
- . maybe_direction ( direction) ,
1366
+ . maybe_direction ( direction)
1367
+ . maybe_content_types (
1368
+ opts. content_types
1369
+ . map ( |types| types. into_iter ( ) . map ( |t| t. into ( ) ) . collect ( ) ) ,
1370
+ ) ,
1335
1371
) ?
1336
1372
. into_iter ( )
1337
1373
. map ( |msg| msg. into ( ) )
@@ -1877,19 +1913,25 @@ mod tests {
1877
1913
} ;
1878
1914
use crate :: {
1879
1915
connect_to_backend, get_inbox_id_for_address, inbox_owner:: SigningError , FfiConsent ,
1880
- FfiConsentEntityType , FfiConsentState , FfiConversation , FfiConversationCallback ,
1881
- FfiConversationMessageKind , FfiCreateGroupOptions , FfiGroupPermissionsOptions ,
1882
- FfiInboxOwner , FfiListConversationsOptions , FfiListMessagesOptions , FfiMetadataField ,
1883
- FfiPermissionPolicy , FfiPermissionPolicySet , FfiPermissionUpdateType , FfiSubscribeError ,
1916
+ FfiConsentEntityType , FfiConsentState , FfiContentType , FfiConversation ,
1917
+ FfiConversationCallback , FfiConversationMessageKind , FfiCreateGroupOptions , FfiDirection ,
1918
+ FfiGroupPermissionsOptions , FfiInboxOwner , FfiListConversationsOptions ,
1919
+ FfiListMessagesOptions , FfiMetadataField , FfiPermissionPolicy , FfiPermissionPolicySet ,
1920
+ FfiPermissionUpdateType , FfiSubscribeError ,
1884
1921
} ;
1885
1922
use ethers:: utils:: hex;
1886
- use std:: sync:: {
1887
- atomic:: { AtomicU32 , Ordering } ,
1888
- Arc , Mutex ,
1923
+ use prost:: Message ;
1924
+ use std:: {
1925
+ collections:: HashMap ,
1926
+ sync:: {
1927
+ atomic:: { AtomicU32 , Ordering } ,
1928
+ Arc , Mutex ,
1929
+ } ,
1889
1930
} ;
1890
1931
use tokio:: { sync:: Notify , time:: error:: Elapsed } ;
1891
1932
use xmtp_common:: tmp_path;
1892
1933
use xmtp_common:: { wait_for_eq, wait_for_ok} ;
1934
+ use xmtp_content_types:: { read_receipt, text:: TextCodec , ContentCodec } ;
1893
1935
use xmtp_cryptography:: { signature:: RecoverableSignature , utils:: rng} ;
1894
1936
use xmtp_id:: associations:: {
1895
1937
generate_inbox_id,
@@ -1900,6 +1942,7 @@ mod tests {
1900
1942
storage:: EncryptionKey ,
1901
1943
InboxOwner ,
1902
1944
} ;
1945
+ use xmtp_proto:: xmtp:: mls:: message_contents:: { ContentTypeId , EncodedContent } ;
1903
1946
1904
1947
const HISTORY_SYNC_URL : & str = "http://localhost:5558" ;
1905
1948
@@ -4832,4 +4875,95 @@ mod tests {
4832
4875
Ok ( _) => panic ! ( "Expected an error, but got Ok" ) ,
4833
4876
}
4834
4877
}
4878
+
4879
+ #[ tokio:: test( flavor = "multi_thread" , worker_threads = 5 ) ]
4880
+ async fn test_can_list_messages_with_content_types ( ) {
4881
+ // Create test clients
4882
+ let alix = new_test_client ( ) . await ;
4883
+ let bo = new_test_client ( ) . await ;
4884
+
4885
+ // Alix creates group with Bo
4886
+ let alix_group = alix
4887
+ . conversations ( )
4888
+ . create_group (
4889
+ vec ! [ bo. account_address. clone( ) ] ,
4890
+ FfiCreateGroupOptions :: default ( ) ,
4891
+ )
4892
+ . await
4893
+ . unwrap ( ) ;
4894
+
4895
+ // Bo syncs to get the group
4896
+ bo. conversations ( ) . sync ( ) . await . unwrap ( ) ;
4897
+ let bo_group = bo. conversation ( alix_group. id ( ) ) . unwrap ( ) ;
4898
+
4899
+ // Alix sends first message
4900
+ alix_group. send ( "hey" . as_bytes ( ) . to_vec ( ) ) . await . unwrap ( ) ;
4901
+
4902
+ // Bo syncs and responds
4903
+ bo_group. sync ( ) . await . unwrap ( ) ;
4904
+ let bo_message_response = TextCodec :: encode ( "hey alix" . to_string ( ) ) . unwrap ( ) ;
4905
+ let mut buf = Vec :: new ( ) ;
4906
+ bo_message_response. encode ( & mut buf) . unwrap ( ) ;
4907
+ bo_group. send ( buf) . await . unwrap ( ) ;
4908
+
4909
+ // Bo sends read receipt
4910
+ let read_receipt_content_id = ContentTypeId {
4911
+ authority_id : "xmtp.org" . to_string ( ) ,
4912
+ type_id : read_receipt:: ReadReceiptCodec :: TYPE_ID . to_string ( ) ,
4913
+ version_major : 1 ,
4914
+ version_minor : 0 ,
4915
+ } ;
4916
+ let read_receipt_encoded_content = EncodedContent {
4917
+ r#type : Some ( read_receipt_content_id) ,
4918
+ parameters : HashMap :: new ( ) ,
4919
+ fallback : None ,
4920
+ compression : None ,
4921
+ content : vec ! [ ] ,
4922
+ } ;
4923
+
4924
+ let mut buf = Vec :: new ( ) ;
4925
+ read_receipt_encoded_content. encode ( & mut buf) . unwrap ( ) ;
4926
+ bo_group. send ( buf) . await . unwrap ( ) ;
4927
+
4928
+ // Alix syncs and gets all messages
4929
+ alix_group. sync ( ) . await . unwrap ( ) ;
4930
+ let latest_message = alix_group
4931
+ // ... existing code ...
4932
+ . find_messages ( FfiListMessagesOptions {
4933
+ direction : Some ( FfiDirection :: Descending ) ,
4934
+ limit : Some ( 1 ) ,
4935
+ ..Default :: default ( )
4936
+ } )
4937
+ . await
4938
+ . unwrap ( ) ;
4939
+
4940
+ // Verify last message is the read receipt
4941
+ assert_eq ! ( latest_message. len( ) , 1 ) ;
4942
+ let latest_message_encoded_content =
4943
+ EncodedContent :: decode ( latest_message. last ( ) . unwrap ( ) . content . clone ( ) . as_slice ( ) )
4944
+ . unwrap ( ) ;
4945
+ assert_eq ! (
4946
+ latest_message_encoded_content. r#type. unwrap( ) . type_id,
4947
+ "readReceipt"
4948
+ ) ;
4949
+
4950
+ // Get only text messages
4951
+ let text_messages = alix_group
4952
+ . find_messages ( FfiListMessagesOptions {
4953
+ content_types : Some ( vec ! [ FfiContentType :: Text ] ) ,
4954
+ direction : Some ( FfiDirection :: Descending ) ,
4955
+ limit : Some ( 1 ) ,
4956
+ ..Default :: default ( )
4957
+ } )
4958
+ . await
4959
+ . unwrap ( ) ;
4960
+
4961
+ // Verify last message is "hey alix" when filtered
4962
+ assert_eq ! ( text_messages. len( ) , 1 ) ;
4963
+ let latest_message_encoded_content =
4964
+ EncodedContent :: decode ( text_messages. last ( ) . unwrap ( ) . content . clone ( ) . as_slice ( ) )
4965
+ . unwrap ( ) ;
4966
+ let text_message = TextCodec :: decode ( latest_message_encoded_content) . unwrap ( ) ;
4967
+ assert_eq ! ( text_message, "hey alix" ) ;
4968
+ }
4835
4969
}
0 commit comments