@@ -43,7 +43,7 @@ use ruma::{
43
43
} ,
44
44
push:: Ruleset ,
45
45
time:: Instant ,
46
- OwnedRoomId , OwnedUserId , RoomId , UserId ,
46
+ MilliSecondsSinceUnixEpoch , OwnedRoomId , OwnedUserId , RoomId , UserId ,
47
47
} ;
48
48
use tokio:: sync:: { broadcast, Mutex } ;
49
49
#[ cfg( feature = "e2e-encryption" ) ]
@@ -66,7 +66,7 @@ use crate::{
66
66
StateStoreDataValue , StateStoreExt , StoreConfig ,
67
67
} ,
68
68
sync:: { RoomUpdates , SyncResponse } ,
69
- RoomStateFilter , SessionMeta ,
69
+ InviteAcceptanceDetails , RoomStateFilter , SessionMeta ,
70
70
} ;
71
71
72
72
/// A no (network) IO client implementation.
@@ -432,21 +432,36 @@ impl BaseClient {
432
432
///
433
433
/// Update the internal and cached state accordingly. Return the final Room.
434
434
///
435
+ /// # Arguments
436
+ ///
437
+ /// * `room_id` - The unique ID identifying the joined room.
438
+ /// * `inviter` - When joining this room in response to an invitation, the
439
+ /// inviter should be recorded before sending the join request to the
440
+ /// server. Providing the inviter here ensures that the
441
+ /// [`InviteAcceptanceDetails`] are stored for this room.
442
+ ///
435
443
/// # Examples
436
444
///
437
445
/// ```rust
438
446
/// # use matrix_sdk_base::{BaseClient, store::StoreConfig, RoomState, ThreadingSupport};
439
- /// # use ruma::OwnedRoomId;
447
+ /// # use ruma::{ OwnedRoomId, OwnedUserId, RoomId} ;
440
448
/// # async {
441
449
/// # let client = BaseClient::new(StoreConfig::new("example".to_owned()), ThreadingSupport::Disabled);
442
450
/// # async fn send_join_request() -> anyhow::Result<OwnedRoomId> { todo!() }
451
+ /// # async fn maybe_get_inviter(room_id: &RoomId) -> anyhow::Result<Option<OwnedUserId>> { todo!() }
452
+ /// # let room_id: &RoomId = todo!();
453
+ /// let maybe_inviter = maybe_get_inviter(room_id).await?;
443
454
/// let room_id = send_join_request().await?;
444
- /// let room = client.room_joined(&room_id).await?;
455
+ /// let room = client.room_joined(&room_id, maybe_inviter ).await?;
445
456
///
446
457
/// assert_eq!(room.state(), RoomState::Joined);
447
458
/// # anyhow::Ok(()) };
448
459
/// ```
449
- pub async fn room_joined ( & self , room_id : & RoomId ) -> Result < Room > {
460
+ pub async fn room_joined (
461
+ & self ,
462
+ room_id : & RoomId ,
463
+ inviter : Option < OwnedUserId > ,
464
+ ) -> Result < Room > {
450
465
let room = self . state_store . get_or_create_room (
451
466
room_id,
452
467
RoomState :: Joined ,
@@ -459,25 +474,32 @@ impl BaseClient {
459
474
let _sync_lock = self . sync_lock ( ) . lock ( ) . await ;
460
475
461
476
let mut room_info = room. clone_info ( ) ;
477
+ let previous_state = room. state ( ) ;
478
+
479
+ room_info. mark_as_joined ( ) ;
480
+ room_info. mark_state_partially_synced ( ) ;
481
+ room_info. mark_members_missing ( ) ; // the own member event changed
462
482
463
483
// If our previous state was an invite and we're now in the joined state, this
464
- // means that the user has explicitly accepted the invite. Let's
465
- // remember when this has happened .
484
+ // means that the user has explicitly accepted an invite. Let's
485
+ // remember some details about the invite .
466
486
//
467
487
// This is somewhat of a workaround for our lack of cryptographic membership.
468
488
// Later on we will decide if historic room keys should be accepted
469
489
// based on this info. If a user has accepted an invite and we receive a room
470
490
// key bundle shortly after, we might accept it. If we don't do
471
491
// this, the homeserver could trick us into accepting any historic room key
472
492
// bundle.
473
- if room. state ( ) == RoomState :: Invited {
474
- room_info. set_invite_accepted_now ( ) ;
493
+ if previous_state == RoomState :: Invited {
494
+ if let Some ( inviter) = inviter {
495
+ let details = InviteAcceptanceDetails {
496
+ invite_accepted_at : MilliSecondsSinceUnixEpoch :: now ( ) ,
497
+ inviter,
498
+ } ;
499
+ room_info. set_invite_acceptance_details ( details) ;
500
+ }
475
501
}
476
502
477
- room_info. mark_as_joined ( ) ;
478
- room_info. mark_state_partially_synced ( ) ;
479
- room_info. mark_members_missing ( ) ; // the own member event changed
480
-
481
503
let mut changes = StateChanges :: default ( ) ;
482
504
changes. add_room ( room_info. clone ( ) ) ;
483
505
@@ -1131,7 +1153,7 @@ impl From<&v5::Request> for RequestedRequiredStates {
1131
1153
mod tests {
1132
1154
use std:: collections:: HashMap ;
1133
1155
1134
- use assert_matches2:: assert_let;
1156
+ use assert_matches2:: { assert_let, assert_matches } ;
1135
1157
use futures_util:: FutureExt as _;
1136
1158
use matrix_sdk_test:: {
1137
1159
async_test, event_factory:: EventFactory , ruma_response_from_json, InvitedRoomBuilder ,
@@ -1739,8 +1761,9 @@ mod tests {
1739
1761
}
1740
1762
1741
1763
#[ async_test]
1742
- async fn test_joined_at_timestamp_is_set ( ) {
1743
- let client = logged_in_base_client ( None ) . await ;
1764
+ async fn test_invite_details_are_set ( ) {
1765
+ let user_id = user_id ! ( "@alice:localhost" ) ;
1766
+ let client = logged_in_base_client ( Some ( user_id) ) . await ;
1744
1767
let invited_room_id = room_id ! ( "!invited:localhost" ) ;
1745
1768
let unknown_room_id = room_id ! ( "!unknown:localhost" ) ;
1746
1769
@@ -1757,27 +1780,41 @@ mod tests {
1757
1780
. expect ( "The sync should have created a room in the invited state" ) ;
1758
1781
1759
1782
assert_eq ! ( invited_room. state( ) , RoomState :: Invited ) ;
1760
- assert ! ( invited_room. inner . get ( ) . invite_accepted_at ( ) . is_none( ) ) ;
1783
+ assert ! ( invited_room. invite_acceptance_details ( ) . is_none( ) ) ;
1761
1784
1762
1785
// Now we join the room.
1763
1786
let joined_room = client
1764
- . room_joined ( invited_room_id)
1787
+ . room_joined ( invited_room_id, Some ( user_id . to_owned ( ) ) )
1765
1788
. await
1766
1789
. expect ( "We should be able to mark a room as joined" ) ;
1767
1790
1768
- // Yup, there's a timestamp now .
1791
+ // Yup, we now have some invite details .
1769
1792
assert_eq ! ( joined_room. state( ) , RoomState :: Joined ) ;
1770
- assert ! ( joined_room. inner. get( ) . invite_accepted_at( ) . is_some( ) ) ;
1793
+ assert_matches ! ( joined_room. invite_acceptance_details( ) , Some ( details) ) ;
1794
+ assert_eq ! ( details. inviter, user_id) ;
1771
1795
1772
1796
// If we didn't know about the room before the join, we assume that there wasn't
1773
1797
// an invite and we don't record the timestamp.
1774
1798
assert ! ( client. get_room( unknown_room_id) . is_none( ) ) ;
1775
1799
let unknown_room = client
1776
- . room_joined ( unknown_room_id)
1800
+ . room_joined ( unknown_room_id, Some ( user_id . to_owned ( ) ) )
1777
1801
. await
1778
1802
. expect ( "We should be able to mark a room as joined" ) ;
1779
1803
1780
1804
assert_eq ! ( unknown_room. state( ) , RoomState :: Joined ) ;
1781
- assert ! ( unknown_room. inner. get( ) . invite_accepted_at( ) . is_none( ) ) ;
1805
+ assert ! ( unknown_room. invite_acceptance_details( ) . is_none( ) ) ;
1806
+
1807
+ sync_builder. clear ( ) ;
1808
+ let response =
1809
+ sync_builder. add_left_room ( LeftRoomBuilder :: new ( invited_room_id) ) . build_sync_response ( ) ;
1810
+ client. receive_sync_response ( response) . await . unwrap ( ) ;
1811
+
1812
+ // Now that we left the room, we shouldn't have any details anymore.
1813
+ let left_room = client
1814
+ . get_room ( invited_room_id)
1815
+ . expect ( "The sync should have created a room in the invited state" ) ;
1816
+
1817
+ assert_eq ! ( left_room. state( ) , RoomState :: Left ) ;
1818
+ assert ! ( left_room. invite_acceptance_details( ) . is_none( ) ) ;
1782
1819
}
1783
1820
}
0 commit comments