atagen

matrix-rust-sdk

public
Star0
Watch0
ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk.git

Blame: crates/matrix-sdk-base/src/client.rs

7ff8adafe9e4atagen2026-10-22 12:30 UTC1
// Copyright 2020 Damir Jelić
7ff8adafe9e4atagen2026-10-22 12:30 UTC2
// Copyright 2020 The Matrix.org Foundation C.I.C.
7ff8adafe9e4atagen2026-10-22 12:30 UTC3
//
7ff8adafe9e4atagen2026-10-22 12:30 UTC4
// Licensed under the Apache License, Version 2.0 (the "License");
7ff8adafe9e4atagen2026-10-22 12:30 UTC5
// you may not use this file except in compliance with the License.
7ff8adafe9e4atagen2026-10-22 12:30 UTC6
// You may obtain a copy of the License at
7ff8adafe9e4atagen2026-10-22 12:30 UTC7
//
7ff8adafe9e4atagen2026-10-22 12:30 UTC8
//     http://www.apache.org/licenses/LICENSE-2.0
7ff8adafe9e4atagen2026-10-22 12:30 UTC9
//
7ff8adafe9e4atagen2026-10-22 12:30 UTC10
// Unless required by applicable law or agreed to in writing, software
7ff8adafe9e4atagen2026-10-22 12:30 UTC11
// distributed under the License is distributed on an "AS IS" BASIS,
7ff8adafe9e4atagen2026-10-22 12:30 UTC12
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
7ff8adafe9e4atagen2026-10-22 12:30 UTC13
// See the License for the specific language governing permissions and
7ff8adafe9e4atagen2026-10-22 12:30 UTC14
// limitations under the License.
7ff8adafe9e4atagen2026-10-22 12:30 UTC15
7ff8adafe9e4atagen2026-10-22 12:30 UTC16
#[cfg(feature = "e2e-encryption")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC17
use std::sync::Arc;
7ff8adafe9e4atagen2026-10-22 12:30 UTC18
use std::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC19
    collections::{BTreeMap, BTreeSet, HashMap},
7ff8adafe9e4atagen2026-10-22 12:30 UTC20
    fmt,
7ff8adafe9e4atagen2026-10-22 12:30 UTC21
    ops::Deref,
7ff8adafe9e4atagen2026-10-22 12:30 UTC22
};
7ff8adafe9e4atagen2026-10-22 12:30 UTC23
7ff8adafe9e4atagen2026-10-22 12:30 UTC24
use eyeball::{SharedObservable, Subscriber};
7ff8adafe9e4atagen2026-10-22 12:30 UTC25
use eyeball_im::{Vector, VectorDiff};
7ff8adafe9e4atagen2026-10-22 12:30 UTC26
use futures_util::Stream;
7ff8adafe9e4atagen2026-10-22 12:30 UTC27
use matrix_sdk_common::{cross_process_lock::CrossProcessLockConfig, timer};
7ff8adafe9e4atagen2026-10-22 12:30 UTC28
#[cfg(feature = "e2e-encryption")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC29
use matrix_sdk_crypto::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC30
    CollectStrategy, DecryptionSettings, EncryptionSettings, OlmError, OlmMachine,
7ff8adafe9e4atagen2026-10-22 12:30 UTC31
    TrustRequirement, store::DynCryptoStore, store::types::RoomPendingKeyBundleDetails,
7ff8adafe9e4atagen2026-10-22 12:30 UTC32
    types::requests::ToDeviceRequest,
7ff8adafe9e4atagen2026-10-22 12:30 UTC33
};
7ff8adafe9e4atagen2026-10-22 12:30 UTC34
#[cfg(doc)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC35
use ruma::DeviceId;
7ff8adafe9e4atagen2026-10-22 12:30 UTC36
#[cfg(feature = "e2e-encryption")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC37
use ruma::events::room::{history_visibility::HistoryVisibility, member::MembershipState};
7ff8adafe9e4atagen2026-10-22 12:30 UTC38
use ruma::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC39
    OwnedRoomId, OwnedUserId, RoomId, UserId,
7ff8adafe9e4atagen2026-10-22 12:30 UTC40
    api::client::{self as api, sync::sync_events::v5},
7ff8adafe9e4atagen2026-10-22 12:30 UTC41
    events::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC42
        StateEvent, StateEventType,
7ff8adafe9e4atagen2026-10-22 12:30 UTC43
        ignored_user_list::IgnoredUserListEventContent,
7ff8adafe9e4atagen2026-10-22 12:30 UTC44
        push_rules::{PushRulesEvent, PushRulesEventContent},
7ff8adafe9e4atagen2026-10-22 12:30 UTC45
        room::member::SyncRoomMemberEvent,
7ff8adafe9e4atagen2026-10-22 12:30 UTC46
    },
7ff8adafe9e4atagen2026-10-22 12:30 UTC47
    push::Ruleset,
7ff8adafe9e4atagen2026-10-22 12:30 UTC48
    time::Instant,
7ff8adafe9e4atagen2026-10-22 12:30 UTC49
};
7ff8adafe9e4atagen2026-10-22 12:30 UTC50
use tokio::sync::{Mutex, broadcast};
7ff8adafe9e4atagen2026-10-22 12:30 UTC51
#[cfg(feature = "e2e-encryption")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC52
use tokio::sync::{RwLock, RwLockReadGuard};
7ff8adafe9e4atagen2026-10-22 12:30 UTC53
use tracing::{Level, debug, enabled, info, instrument, warn};
7ff8adafe9e4atagen2026-10-22 12:30 UTC54
7ff8adafe9e4atagen2026-10-22 12:30 UTC55
#[cfg(feature = "e2e-encryption")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC56
use crate::RoomMemberships;
7ff8adafe9e4atagen2026-10-22 12:30 UTC57
use crate::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC58
    RoomStateFilter, SessionMeta, StateStore,
7ff8adafe9e4atagen2026-10-22 12:30 UTC59
    deserialized_responses::DisplayName,
7ff8adafe9e4atagen2026-10-22 12:30 UTC60
    error::{Error, Result},
7ff8adafe9e4atagen2026-10-22 12:30 UTC61
    event_cache::store::{EventCacheStoreLock, EventCacheStoreLockState},
7ff8adafe9e4atagen2026-10-22 12:30 UTC62
    media::store::MediaStoreLock,
7ff8adafe9e4atagen2026-10-22 12:30 UTC63
    response_processors::{self as processors, Context},
7ff8adafe9e4atagen2026-10-22 12:30 UTC64
    room::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC65
        Room, RoomInfoNotableUpdate, RoomInfoNotableUpdateReasons, RoomMembersUpdate, RoomState,
7ff8adafe9e4atagen2026-10-22 12:30 UTC66
    },
7ff8adafe9e4atagen2026-10-22 12:30 UTC67
    store::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC68
        AvatarCache, BaseStateStore, DynStateStore, MemoryStore, Result as StoreResult,
7ff8adafe9e4atagen2026-10-22 12:30 UTC69
        RoomLoadSettings, StateChanges, StateStoreDataKey, StateStoreDataValue, StateStoreExt,
7ff8adafe9e4atagen2026-10-22 12:30 UTC70
        StoreConfig, ambiguity_map::AmbiguityCache,
7ff8adafe9e4atagen2026-10-22 12:30 UTC71
    },
7ff8adafe9e4atagen2026-10-22 12:30 UTC72
    sync::{RoomUpdates, SyncResponse},
7ff8adafe9e4atagen2026-10-22 12:30 UTC73
};
7ff8adafe9e4atagen2026-10-22 12:30 UTC74
7ff8adafe9e4atagen2026-10-22 12:30 UTC75
/// A no (network) IO client implementation.
7ff8adafe9e4atagen2026-10-22 12:30 UTC76
///
7ff8adafe9e4atagen2026-10-22 12:30 UTC77
/// This client is a state machine that receives responses and events and
7ff8adafe9e4atagen2026-10-22 12:30 UTC78
/// accordingly updates its state. It is not designed to be used directly, but
7ff8adafe9e4atagen2026-10-22 12:30 UTC79
/// rather through `matrix_sdk::Client`.
7ff8adafe9e4atagen2026-10-22 12:30 UTC80
///
7ff8adafe9e4atagen2026-10-22 12:30 UTC81
/// ```rust
7ff8adafe9e4atagen2026-10-22 12:30 UTC82
/// use matrix_sdk_base::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC83
///     BaseClient, DmRoomDefinition, ThreadingSupport, store::StoreConfig,
7ff8adafe9e4atagen2026-10-22 12:30 UTC84
/// };
7ff8adafe9e4atagen2026-10-22 12:30 UTC85
/// use matrix_sdk_common::cross_process_lock::CrossProcessLockConfig;
7ff8adafe9e4atagen2026-10-22 12:30 UTC86
///
7ff8adafe9e4atagen2026-10-22 12:30 UTC87
/// let client = BaseClient::new(
7ff8adafe9e4atagen2026-10-22 12:30 UTC88
///     StoreConfig::new(CrossProcessLockConfig::multi_process(
7ff8adafe9e4atagen2026-10-22 12:30 UTC89
///         "cross-process-holder-name".to_owned(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC90
///     )),
7ff8adafe9e4atagen2026-10-22 12:30 UTC91
///     ThreadingSupport::Disabled,
7ff8adafe9e4atagen2026-10-22 12:30 UTC92
///     DmRoomDefinition::default(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC93
/// );
7ff8adafe9e4atagen2026-10-22 12:30 UTC94
/// ```
7ff8adafe9e4atagen2026-10-22 12:30 UTC95
#[derive(Clone)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC96
pub struct BaseClient {
7ff8adafe9e4atagen2026-10-22 12:30 UTC97
    /// The state store.
7ff8adafe9e4atagen2026-10-22 12:30 UTC98
    pub(crate) state_store: BaseStateStore,
7ff8adafe9e4atagen2026-10-22 12:30 UTC99
7ff8adafe9e4atagen2026-10-22 12:30 UTC100
    /// The store used by the event cache.
7ff8adafe9e4atagen2026-10-22 12:30 UTC101
    event_cache_store: EventCacheStoreLock,
7ff8adafe9e4atagen2026-10-22 12:30 UTC102
7ff8adafe9e4atagen2026-10-22 12:30 UTC103
    /// The store used by the media cache.
7ff8adafe9e4atagen2026-10-22 12:30 UTC104
    media_store: MediaStoreLock,
7ff8adafe9e4atagen2026-10-22 12:30 UTC105
7ff8adafe9e4atagen2026-10-22 12:30 UTC106
    /// The store used for encryption.
7ff8adafe9e4atagen2026-10-22 12:30 UTC107
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC108
    /// This field is only meant to be used for `OlmMachine` initialization.
7ff8adafe9e4atagen2026-10-22 12:30 UTC109
    /// All operations on it happen inside the `OlmMachine`.
7ff8adafe9e4atagen2026-10-22 12:30 UTC110
    #[cfg(feature = "e2e-encryption")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC111
    crypto_store: Arc<DynCryptoStore>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC112
7ff8adafe9e4atagen2026-10-22 12:30 UTC113
    /// The olm-machine that is created once the
7ff8adafe9e4atagen2026-10-22 12:30 UTC114
    /// [`SessionMeta`][crate::session::SessionMeta] is set via
7ff8adafe9e4atagen2026-10-22 12:30 UTC115
    /// [`BaseClient::activate`]
7ff8adafe9e4atagen2026-10-22 12:30 UTC116
    #[cfg(feature = "e2e-encryption")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC117
    olm_machine: Arc<RwLock<Option<OlmMachine>>>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC118
7ff8adafe9e4atagen2026-10-22 12:30 UTC119
    /// Observable of when a user is ignored/unignored.
7ff8adafe9e4atagen2026-10-22 12:30 UTC120
    pub(crate) ignore_user_list_changes: SharedObservable<Vec<String>>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC121
7ff8adafe9e4atagen2026-10-22 12:30 UTC122
    /// The strategy to use for picking recipient devices, when sending an
7ff8adafe9e4atagen2026-10-22 12:30 UTC123
    /// encrypted message.
7ff8adafe9e4atagen2026-10-22 12:30 UTC124
    #[cfg(feature = "e2e-encryption")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC125
    pub room_key_recipient_strategy: CollectStrategy,
7ff8adafe9e4atagen2026-10-22 12:30 UTC126
7ff8adafe9e4atagen2026-10-22 12:30 UTC127
    /// The settings to use for decrypting events.
7ff8adafe9e4atagen2026-10-22 12:30 UTC128
    #[cfg(feature = "e2e-encryption")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC129
    pub decryption_settings: DecryptionSettings,
7ff8adafe9e4atagen2026-10-22 12:30 UTC130
7ff8adafe9e4atagen2026-10-22 12:30 UTC131
    /// If the client should handle verification events received when syncing.
7ff8adafe9e4atagen2026-10-22 12:30 UTC132
    #[cfg(feature = "e2e-encryption")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC133
    pub handle_verification_events: bool,
7ff8adafe9e4atagen2026-10-22 12:30 UTC134
7ff8adafe9e4atagen2026-10-22 12:30 UTC135
    /// Whether the client supports threads or not.
7ff8adafe9e4atagen2026-10-22 12:30 UTC136
    pub threading_support: ThreadingSupport,
7ff8adafe9e4atagen2026-10-22 12:30 UTC137
7ff8adafe9e4atagen2026-10-22 12:30 UTC138
    /// The definition of what is considered a DM room.
7ff8adafe9e4atagen2026-10-22 12:30 UTC139
    pub dm_room_definition: DmRoomDefinition,
7ff8adafe9e4atagen2026-10-22 12:30 UTC140
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC141
7ff8adafe9e4atagen2026-10-22 12:30 UTC142
#[cfg(not(tarpaulin_include))]
7ff8adafe9e4atagen2026-10-22 12:30 UTC143
impl fmt::Debug for BaseClient {
7ff8adafe9e4atagen2026-10-22 12:30 UTC144
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
7ff8adafe9e4atagen2026-10-22 12:30 UTC145
        f.debug_struct("BaseClient")
7ff8adafe9e4atagen2026-10-22 12:30 UTC146
            .field("session_meta", &self.state_store.session_meta())
7ff8adafe9e4atagen2026-10-22 12:30 UTC147
            .field("sync_token", &self.state_store.sync_token)
7ff8adafe9e4atagen2026-10-22 12:30 UTC148
            .finish_non_exhaustive()
7ff8adafe9e4atagen2026-10-22 12:30 UTC149
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC150
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC151
7ff8adafe9e4atagen2026-10-22 12:30 UTC152
/// Whether this client instance supports threading or not. Currently used to
7ff8adafe9e4atagen2026-10-22 12:30 UTC153
/// determine how the client handles read receipts and unread count computations
7ff8adafe9e4atagen2026-10-22 12:30 UTC154
/// on the base SDK level.
7ff8adafe9e4atagen2026-10-22 12:30 UTC155
///
7ff8adafe9e4atagen2026-10-22 12:30 UTC156
/// Timelines on the other hand have a separate `TimelineFocus`
7ff8adafe9e4atagen2026-10-22 12:30 UTC157
/// `hide_threaded_events` associated value that can be used to hide threaded
7ff8adafe9e4atagen2026-10-22 12:30 UTC158
/// events but also to enable threaded read receipt sending. This is because
7ff8adafe9e4atagen2026-10-22 12:30 UTC159
/// certain timeline instances should ignore threading no matter what's defined
7ff8adafe9e4atagen2026-10-22 12:30 UTC160
/// at the client level. One such example are media filtered timelines which
7ff8adafe9e4atagen2026-10-22 12:30 UTC161
/// should contain all the room's media no matter what thread its in (unless
7ff8adafe9e4atagen2026-10-22 12:30 UTC162
/// explicitly opted into).
7ff8adafe9e4atagen2026-10-22 12:30 UTC163
#[derive(Clone, Copy, Debug)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC164
pub enum ThreadingSupport {
7ff8adafe9e4atagen2026-10-22 12:30 UTC165
    /// Threading enabled.
7ff8adafe9e4atagen2026-10-22 12:30 UTC166
    Enabled {
7ff8adafe9e4atagen2026-10-22 12:30 UTC167
        /// Enable client-wide thread subscriptions support (MSC4306 / MSC4308).
7ff8adafe9e4atagen2026-10-22 12:30 UTC168
        ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC169
        /// This may cause filtering out of thread subscriptions, and loading
7ff8adafe9e4atagen2026-10-22 12:30 UTC170
        /// the thread subscriptions via the sliding sync extension,
7ff8adafe9e4atagen2026-10-22 12:30 UTC171
        /// when the room list service is being used.
7ff8adafe9e4atagen2026-10-22 12:30 UTC172
        with_subscriptions: bool,
7ff8adafe9e4atagen2026-10-22 12:30 UTC173
    },
7ff8adafe9e4atagen2026-10-22 12:30 UTC174
    /// Threading disabled.
7ff8adafe9e4atagen2026-10-22 12:30 UTC175
    Disabled,
7ff8adafe9e4atagen2026-10-22 12:30 UTC176
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC177
7ff8adafe9e4atagen2026-10-22 12:30 UTC178
impl BaseClient {
7ff8adafe9e4atagen2026-10-22 12:30 UTC179
    /// Create a new client.
7ff8adafe9e4atagen2026-10-22 12:30 UTC180
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC181
    /// # Arguments
7ff8adafe9e4atagen2026-10-22 12:30 UTC182
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC183
    /// * `config` - the configuration for the stores (state store, event cache
7ff8adafe9e4atagen2026-10-22 12:30 UTC184
    ///   store and crypto store).
7ff8adafe9e4atagen2026-10-22 12:30 UTC185
    pub fn new(
7ff8adafe9e4atagen2026-10-22 12:30 UTC186
        config: StoreConfig,
7ff8adafe9e4atagen2026-10-22 12:30 UTC187
        threading_support: ThreadingSupport,
7ff8adafe9e4atagen2026-10-22 12:30 UTC188
        dm_room_definition: DmRoomDefinition,
7ff8adafe9e4atagen2026-10-22 12:30 UTC189
    ) -> Self {
7ff8adafe9e4atagen2026-10-22 12:30 UTC190
        let store = BaseStateStore::new(config.state_store);
7ff8adafe9e4atagen2026-10-22 12:30 UTC191
7ff8adafe9e4atagen2026-10-22 12:30 UTC192
        BaseClient {
7ff8adafe9e4atagen2026-10-22 12:30 UTC193
            state_store: store,
7ff8adafe9e4atagen2026-10-22 12:30 UTC194
            event_cache_store: config.event_cache_store,
7ff8adafe9e4atagen2026-10-22 12:30 UTC195
            media_store: config.media_store,
7ff8adafe9e4atagen2026-10-22 12:30 UTC196
            #[cfg(feature = "e2e-encryption")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC197
            crypto_store: config.crypto_store,
7ff8adafe9e4atagen2026-10-22 12:30 UTC198
            #[cfg(feature = "e2e-encryption")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC199
            olm_machine: Default::default(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC200
            ignore_user_list_changes: Default::default(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC201
            #[cfg(feature = "e2e-encryption")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC202
            room_key_recipient_strategy: Default::default(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC203
            #[cfg(feature = "e2e-encryption")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC204
            decryption_settings: DecryptionSettings {
7ff8adafe9e4atagen2026-10-22 12:30 UTC205
                sender_device_trust_requirement: TrustRequirement::Untrusted,
7ff8adafe9e4atagen2026-10-22 12:30 UTC206
            },
7ff8adafe9e4atagen2026-10-22 12:30 UTC207
            #[cfg(feature = "e2e-encryption")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC208
            handle_verification_events: true,
7ff8adafe9e4atagen2026-10-22 12:30 UTC209
            threading_support,
7ff8adafe9e4atagen2026-10-22 12:30 UTC210
            dm_room_definition,
7ff8adafe9e4atagen2026-10-22 12:30 UTC211
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC212
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC213
7ff8adafe9e4atagen2026-10-22 12:30 UTC214
    /// Clones the current base client to use the same crypto store but a
7ff8adafe9e4atagen2026-10-22 12:30 UTC215
    /// different, in-memory store config, and resets transient state.
7ff8adafe9e4atagen2026-10-22 12:30 UTC216
    #[cfg(feature = "e2e-encryption")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC217
    pub async fn clone_with_in_memory_state_store(
7ff8adafe9e4atagen2026-10-22 12:30 UTC218
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC219
        cross_process_mode: CrossProcessLockConfig,
7ff8adafe9e4atagen2026-10-22 12:30 UTC220
        handle_verification_events: bool,
7ff8adafe9e4atagen2026-10-22 12:30 UTC221
    ) -> Result<Self> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC222
        let config = StoreConfig::new(cross_process_mode).state_store(MemoryStore::new());
7ff8adafe9e4atagen2026-10-22 12:30 UTC223
        let config = config.crypto_store(self.crypto_store.clone());
7ff8adafe9e4atagen2026-10-22 12:30 UTC224
7ff8adafe9e4atagen2026-10-22 12:30 UTC225
        let copy = Self {
7ff8adafe9e4atagen2026-10-22 12:30 UTC226
            state_store: BaseStateStore::new(config.state_store),
7ff8adafe9e4atagen2026-10-22 12:30 UTC227
            event_cache_store: config.event_cache_store,
7ff8adafe9e4atagen2026-10-22 12:30 UTC228
            media_store: config.media_store,
7ff8adafe9e4atagen2026-10-22 12:30 UTC229
            // We copy the crypto store as well as the `OlmMachine` for two reasons:
7ff8adafe9e4atagen2026-10-22 12:30 UTC230
            // 1. The `self.crypto_store` is the same as the one used inside the `OlmMachine`.
7ff8adafe9e4atagen2026-10-22 12:30 UTC231
            // 2. We need to ensure that the parent and child use the same data and caches inside
7ff8adafe9e4atagen2026-10-22 12:30 UTC232
            //    the `OlmMachine` so the various ratchets and places where new randomness gets
7ff8adafe9e4atagen2026-10-22 12:30 UTC233
            //    introduced don't diverge, i.e. one-time keys that get generated by the Olm Account
7ff8adafe9e4atagen2026-10-22 12:30 UTC234
            //    or Olm sessions when they encrypt or decrypt messages.
7ff8adafe9e4atagen2026-10-22 12:30 UTC235
            crypto_store: self.crypto_store.clone(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC236
            olm_machine: self.olm_machine.clone(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC237
            ignore_user_list_changes: Default::default(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC238
            room_key_recipient_strategy: self.room_key_recipient_strategy.clone(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC239
            decryption_settings: self.decryption_settings.clone(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC240
            handle_verification_events,
7ff8adafe9e4atagen2026-10-22 12:30 UTC241
            threading_support: self.threading_support,
7ff8adafe9e4atagen2026-10-22 12:30 UTC242
            dm_room_definition: self.dm_room_definition.clone(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC243
        };
7ff8adafe9e4atagen2026-10-22 12:30 UTC244
7ff8adafe9e4atagen2026-10-22 12:30 UTC245
        copy.state_store.derive_from_other(&self.state_store).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC246
7ff8adafe9e4atagen2026-10-22 12:30 UTC247
        Ok(copy)
7ff8adafe9e4atagen2026-10-22 12:30 UTC248
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC249
7ff8adafe9e4atagen2026-10-22 12:30 UTC250
    /// Clones the current base client to use the same crypto store but a
7ff8adafe9e4atagen2026-10-22 12:30 UTC251
    /// different, in-memory store config, and resets transient state.
7ff8adafe9e4atagen2026-10-22 12:30 UTC252
    #[cfg(not(feature = "e2e-encryption"))]
7ff8adafe9e4atagen2026-10-22 12:30 UTC253
    #[allow(clippy::unused_async)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC254
    pub async fn clone_with_in_memory_state_store(
7ff8adafe9e4atagen2026-10-22 12:30 UTC255
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC256
        cross_process_store_config: CrossProcessLockConfig,
7ff8adafe9e4atagen2026-10-22 12:30 UTC257
        _handle_verification_events: bool,
7ff8adafe9e4atagen2026-10-22 12:30 UTC258
    ) -> Result<Self> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC259
        let config = StoreConfig::new(cross_process_store_config).state_store(MemoryStore::new());
7ff8adafe9e4atagen2026-10-22 12:30 UTC260
        Ok(Self::new(config, ThreadingSupport::Disabled, DmRoomDefinition::default()))
7ff8adafe9e4atagen2026-10-22 12:30 UTC261
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC262
7ff8adafe9e4atagen2026-10-22 12:30 UTC263
    /// Get the session meta information.
7ff8adafe9e4atagen2026-10-22 12:30 UTC264
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC265
    /// If the client is currently logged in, this will return a
7ff8adafe9e4atagen2026-10-22 12:30 UTC266
    /// [`SessionMeta`] object which contains the user ID and device ID.
7ff8adafe9e4atagen2026-10-22 12:30 UTC267
    /// Otherwise it returns `None`.
7ff8adafe9e4atagen2026-10-22 12:30 UTC268
    pub fn session_meta(&self) -> Option<&SessionMeta> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC269
        self.state_store.session_meta()
7ff8adafe9e4atagen2026-10-22 12:30 UTC270
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC271
7ff8adafe9e4atagen2026-10-22 12:30 UTC272
    /// Get all the rooms this client knows about.
7ff8adafe9e4atagen2026-10-22 12:30 UTC273
    pub fn rooms(&self) -> Vec<Room> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC274
        self.state_store.rooms()
7ff8adafe9e4atagen2026-10-22 12:30 UTC275
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC276
7ff8adafe9e4atagen2026-10-22 12:30 UTC277
    /// Get all the rooms this client knows about, filtered by room state.
7ff8adafe9e4atagen2026-10-22 12:30 UTC278
    pub fn rooms_filtered(&self, filter: RoomStateFilter) -> Vec<Room> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC279
        self.state_store.rooms_filtered(filter)
7ff8adafe9e4atagen2026-10-22 12:30 UTC280
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC281
7ff8adafe9e4atagen2026-10-22 12:30 UTC282
    /// Get a stream of all the rooms changes, in addition to the existing
7ff8adafe9e4atagen2026-10-22 12:30 UTC283
    /// rooms.
7ff8adafe9e4atagen2026-10-22 12:30 UTC284
    pub fn rooms_stream(
7ff8adafe9e4atagen2026-10-22 12:30 UTC285
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC286
    ) -> (Vector<Room>, impl Stream<Item = Vec<VectorDiff<Room>>> + use<>) {
7ff8adafe9e4atagen2026-10-22 12:30 UTC287
        self.state_store.rooms_stream()
7ff8adafe9e4atagen2026-10-22 12:30 UTC288
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC289
7ff8adafe9e4atagen2026-10-22 12:30 UTC290
    /// Lookup the Room for the given RoomId, or create one, if it didn't exist
7ff8adafe9e4atagen2026-10-22 12:30 UTC291
    /// yet in the store
7ff8adafe9e4atagen2026-10-22 12:30 UTC292
    pub fn get_or_create_room(&self, room_id: &RoomId, room_state: RoomState) -> Room {
7ff8adafe9e4atagen2026-10-22 12:30 UTC293
        self.state_store.get_or_create_room(room_id, room_state)
7ff8adafe9e4atagen2026-10-22 12:30 UTC294
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC295
7ff8adafe9e4atagen2026-10-22 12:30 UTC296
    /// Get a reference to the state store.
7ff8adafe9e4atagen2026-10-22 12:30 UTC297
    pub fn state_store(&self) -> &DynStateStore {
7ff8adafe9e4atagen2026-10-22 12:30 UTC298
        self.state_store.deref()
7ff8adafe9e4atagen2026-10-22 12:30 UTC299
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC300
7ff8adafe9e4atagen2026-10-22 12:30 UTC301
    /// Get a reference to the event cache store.
7ff8adafe9e4atagen2026-10-22 12:30 UTC302
    pub fn event_cache_store(&self) -> &EventCacheStoreLock {
7ff8adafe9e4atagen2026-10-22 12:30 UTC303
        &self.event_cache_store
7ff8adafe9e4atagen2026-10-22 12:30 UTC304
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC305
7ff8adafe9e4atagen2026-10-22 12:30 UTC306
    /// Get a reference to the media store.
7ff8adafe9e4atagen2026-10-22 12:30 UTC307
    pub fn media_store(&self) -> &MediaStoreLock {
7ff8adafe9e4atagen2026-10-22 12:30 UTC308
        &self.media_store
7ff8adafe9e4atagen2026-10-22 12:30 UTC309
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC310
7ff8adafe9e4atagen2026-10-22 12:30 UTC311
    /// Check whether the client has been activated.
7ff8adafe9e4atagen2026-10-22 12:30 UTC312
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC313
    /// See [`BaseClient::activate`] to know what it means.
7ff8adafe9e4atagen2026-10-22 12:30 UTC314
    pub fn is_active(&self) -> bool {
7ff8adafe9e4atagen2026-10-22 12:30 UTC315
        self.state_store.session_meta().is_some()
7ff8adafe9e4atagen2026-10-22 12:30 UTC316
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC317
7ff8adafe9e4atagen2026-10-22 12:30 UTC318
    /// Activate the client.
7ff8adafe9e4atagen2026-10-22 12:30 UTC319
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC320
    /// A client is considered active when:
7ff8adafe9e4atagen2026-10-22 12:30 UTC321
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC322
    /// 1. It has a `SessionMeta` (user ID, device ID and access token),
7ff8adafe9e4atagen2026-10-22 12:30 UTC323
    /// 2. Has loaded cached data from storage,
7ff8adafe9e4atagen2026-10-22 12:30 UTC324
    /// 3. If encryption is enabled, it also initialized or restored its
7ff8adafe9e4atagen2026-10-22 12:30 UTC325
    ///    `OlmMachine`.
7ff8adafe9e4atagen2026-10-22 12:30 UTC326
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC327
    /// # Arguments
7ff8adafe9e4atagen2026-10-22 12:30 UTC328
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC329
    /// * `session_meta` - The meta of a session that the user already has from
7ff8adafe9e4atagen2026-10-22 12:30 UTC330
    ///   a previous login call.
7ff8adafe9e4atagen2026-10-22 12:30 UTC331
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC332
    /// * `custom_account` - A custom
7ff8adafe9e4atagen2026-10-22 12:30 UTC333
    ///   [`matrix_sdk_crypto::vodozemac::olm::Account`] to be used for the
7ff8adafe9e4atagen2026-10-22 12:30 UTC334
    ///   identity and one-time keys of this [`BaseClient`]. If no account is
7ff8adafe9e4atagen2026-10-22 12:30 UTC335
    ///   provided, a new default one or one from the store will be used. If an
7ff8adafe9e4atagen2026-10-22 12:30 UTC336
    ///   account is provided and one already exists in the store for this
7ff8adafe9e4atagen2026-10-22 12:30 UTC337
    ///   [`UserId`]/[`DeviceId`] combination, an error will be raised. This is
7ff8adafe9e4atagen2026-10-22 12:30 UTC338
    ///   useful if one wishes to create identity keys before knowing the
7ff8adafe9e4atagen2026-10-22 12:30 UTC339
    ///   user/device IDs, e.g., to use the identity key as the device ID.
7ff8adafe9e4atagen2026-10-22 12:30 UTC340
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC341
    /// * `room_load_settings` — Specify how many rooms must be restored; use
7ff8adafe9e4atagen2026-10-22 12:30 UTC342
    ///   `::default()` if you don't know which value to pick.
7ff8adafe9e4atagen2026-10-22 12:30 UTC343
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC344
    /// # Panics
7ff8adafe9e4atagen2026-10-22 12:30 UTC345
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC346
    /// This method panics if it is called twice.
7ff8adafe9e4atagen2026-10-22 12:30 UTC347
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC348
    /// [`UserId`]: ruma::UserId
7ff8adafe9e4atagen2026-10-22 12:30 UTC349
    pub async fn activate(
7ff8adafe9e4atagen2026-10-22 12:30 UTC350
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC351
        session_meta: SessionMeta,
7ff8adafe9e4atagen2026-10-22 12:30 UTC352
        room_load_settings: RoomLoadSettings,
7ff8adafe9e4atagen2026-10-22 12:30 UTC353
        #[cfg(feature = "e2e-encryption")] custom_account: Option<
7ff8adafe9e4atagen2026-10-22 12:30 UTC354
            crate::crypto::vodozemac::olm::Account,
7ff8adafe9e4atagen2026-10-22 12:30 UTC355
        >,
7ff8adafe9e4atagen2026-10-22 12:30 UTC356
    ) -> Result<()> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC357
        debug!(user_id = ?session_meta.user_id, device_id = ?session_meta.device_id, "Activating the client");
7ff8adafe9e4atagen2026-10-22 12:30 UTC358
7ff8adafe9e4atagen2026-10-22 12:30 UTC359
        self.state_store.load_rooms(&session_meta.user_id, room_load_settings).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC360
        self.state_store.load_sync_token().await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC361
        self.state_store.set_session_meta(session_meta);
7ff8adafe9e4atagen2026-10-22 12:30 UTC362
7ff8adafe9e4atagen2026-10-22 12:30 UTC363
        #[cfg(feature = "e2e-encryption")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC364
        self.regenerate_olm(custom_account).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC365
7ff8adafe9e4atagen2026-10-22 12:30 UTC366
        Ok(())
7ff8adafe9e4atagen2026-10-22 12:30 UTC367
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC368
7ff8adafe9e4atagen2026-10-22 12:30 UTC369
    /// Recreate an `OlmMachine` from scratch.
7ff8adafe9e4atagen2026-10-22 12:30 UTC370
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC371
    /// In particular, this will clear all its caches.
7ff8adafe9e4atagen2026-10-22 12:30 UTC372
    #[cfg(feature = "e2e-encryption")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC373
    pub async fn regenerate_olm(
7ff8adafe9e4atagen2026-10-22 12:30 UTC374
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC375
        custom_account: Option<crate::crypto::vodozemac::olm::Account>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC376
    ) -> Result<()> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC377
        tracing::debug!("regenerating OlmMachine");
7ff8adafe9e4atagen2026-10-22 12:30 UTC378
        let session_meta = self.session_meta().ok_or(Error::OlmError(OlmError::MissingSession))?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC379
7ff8adafe9e4atagen2026-10-22 12:30 UTC380
        // Recreate the `OlmMachine` and wipe the in-memory cache in the store
7ff8adafe9e4atagen2026-10-22 12:30 UTC381
        // because we suspect it has stale data.
7ff8adafe9e4atagen2026-10-22 12:30 UTC382
        let olm_machine = OlmMachine::with_store(
7ff8adafe9e4atagen2026-10-22 12:30 UTC383
            &session_meta.user_id,
7ff8adafe9e4atagen2026-10-22 12:30 UTC384
            &session_meta.device_id,
7ff8adafe9e4atagen2026-10-22 12:30 UTC385
            self.crypto_store.clone(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC386
            custom_account,
7ff8adafe9e4atagen2026-10-22 12:30 UTC387
        )
7ff8adafe9e4atagen2026-10-22 12:30 UTC388
        .await
7ff8adafe9e4atagen2026-10-22 12:30 UTC389
        .map_err(OlmError::from)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC390
7ff8adafe9e4atagen2026-10-22 12:30 UTC391
        *self.olm_machine.write().await = Some(olm_machine);
7ff8adafe9e4atagen2026-10-22 12:30 UTC392
        Ok(())
7ff8adafe9e4atagen2026-10-22 12:30 UTC393
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC394
7ff8adafe9e4atagen2026-10-22 12:30 UTC395
    /// Get the current, if any, sync token of the client.
7ff8adafe9e4atagen2026-10-22 12:30 UTC396
    /// This will be None if the client didn't sync at least once.
7ff8adafe9e4atagen2026-10-22 12:30 UTC397
    pub async fn sync_token(&self) -> Option<String> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC398
        self.state_store.sync_token.read().await.clone()
7ff8adafe9e4atagen2026-10-22 12:30 UTC399
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC400
7ff8adafe9e4atagen2026-10-22 12:30 UTC401
    /// User has knocked on a room.
7ff8adafe9e4atagen2026-10-22 12:30 UTC402
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC403
    /// Update the internal and cached state accordingly. Return the final Room.
7ff8adafe9e4atagen2026-10-22 12:30 UTC404
    pub async fn room_knocked(&self, room_id: &RoomId) -> Result<Room> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC405
        let room = self.state_store.get_or_create_room(room_id, RoomState::Knocked);
7ff8adafe9e4atagen2026-10-22 12:30 UTC406
7ff8adafe9e4atagen2026-10-22 12:30 UTC407
        if room.state() != RoomState::Knocked {
7ff8adafe9e4atagen2026-10-22 12:30 UTC408
            let store_guard = self.state_store.lock().lock().await;
7ff8adafe9e4atagen2026-10-22 12:30 UTC409
7ff8adafe9e4atagen2026-10-22 12:30 UTC410
            // We are no longer joined to the room, so the invite acceptance details are no
7ff8adafe9e4atagen2026-10-22 12:30 UTC411
            // longer relevant.
7ff8adafe9e4atagen2026-10-22 12:30 UTC412
            #[cfg(feature = "e2e-encryption")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC413
            if let Some(olm_machine) = self.olm_machine().await.as_ref() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC414
                olm_machine.store().clear_room_pending_key_bundle(room_id).await?
7ff8adafe9e4atagen2026-10-22 12:30 UTC415
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC416
7ff8adafe9e4atagen2026-10-22 12:30 UTC417
            room.update_and_save_room_info_with_store_guard(&store_guard, |mut info| {
7ff8adafe9e4atagen2026-10-22 12:30 UTC418
                info.mark_as_knocked();
7ff8adafe9e4atagen2026-10-22 12:30 UTC419
                info.mark_state_partially_synced();
7ff8adafe9e4atagen2026-10-22 12:30 UTC420
                info.mark_members_missing(); // the own member event changed
7ff8adafe9e4atagen2026-10-22 12:30 UTC421
                (info, RoomInfoNotableUpdateReasons::MEMBERSHIP)
7ff8adafe9e4atagen2026-10-22 12:30 UTC422
            })
7ff8adafe9e4atagen2026-10-22 12:30 UTC423
            .await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC424
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC425
7ff8adafe9e4atagen2026-10-22 12:30 UTC426
        Ok(room)
7ff8adafe9e4atagen2026-10-22 12:30 UTC427
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC428
7ff8adafe9e4atagen2026-10-22 12:30 UTC429
    /// The user has joined a room using this specific client.
7ff8adafe9e4atagen2026-10-22 12:30 UTC430
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC431
    /// This method should be called if the user accepts an invite or if they
7ff8adafe9e4atagen2026-10-22 12:30 UTC432
    /// join a public room.
7ff8adafe9e4atagen2026-10-22 12:30 UTC433
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC434
    /// The method will create a [`Room`] object if one does not exist yet and
7ff8adafe9e4atagen2026-10-22 12:30 UTC435
    /// set the state of the [`Room`] to [`RoomState::Joined`]. The [`Room`]
7ff8adafe9e4atagen2026-10-22 12:30 UTC436
    /// object will be persisted in the cache. Please note that the [`Room`]
7ff8adafe9e4atagen2026-10-22 12:30 UTC437
    /// will be a stub until a sync has been received with the full room
7ff8adafe9e4atagen2026-10-22 12:30 UTC438
    /// state using [`BaseClient::receive_sync_response`].
7ff8adafe9e4atagen2026-10-22 12:30 UTC439
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC440
    /// Update the internal and cached state accordingly. Return the final Room.
7ff8adafe9e4atagen2026-10-22 12:30 UTC441
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC442
    /// # Arguments
7ff8adafe9e4atagen2026-10-22 12:30 UTC443
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC444
    /// * `room_id` - The unique ID identifying the joined room.
7ff8adafe9e4atagen2026-10-22 12:30 UTC445
    /// * `inviter` - When joining this room in response to an invitation, the
7ff8adafe9e4atagen2026-10-22 12:30 UTC446
    ///   inviter should be recorded before sending the join request to the
7ff8adafe9e4atagen2026-10-22 12:30 UTC447
    ///   server. Providing the inviter here ensures that the
7ff8adafe9e4atagen2026-10-22 12:30 UTC448
    ///   [`RoomPendingKeyBundleDetails`] are stored for this room.
7ff8adafe9e4atagen2026-10-22 12:30 UTC449
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC450
    /// # Examples
7ff8adafe9e4atagen2026-10-22 12:30 UTC451
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC452
    /// ```rust
7ff8adafe9e4atagen2026-10-22 12:30 UTC453
    /// # use matrix_sdk_base::{BaseClient, store::StoreConfig, RoomState, ThreadingSupport, DmRoomDefinition};
7ff8adafe9e4atagen2026-10-22 12:30 UTC454
    /// # use ruma::{OwnedRoomId, OwnedUserId, RoomId};
7ff8adafe9e4atagen2026-10-22 12:30 UTC455
    /// use matrix_sdk_common::cross_process_lock::CrossProcessLockConfig;
7ff8adafe9e4atagen2026-10-22 12:30 UTC456
    /// # async {
7ff8adafe9e4atagen2026-10-22 12:30 UTC457
    /// # let client = BaseClient::new(
7ff8adafe9e4atagen2026-10-22 12:30 UTC458
    ///     StoreConfig::new(CrossProcessLockConfig::multi_process("example")),
7ff8adafe9e4atagen2026-10-22 12:30 UTC459
    ///     ThreadingSupport::Disabled,
7ff8adafe9e4atagen2026-10-22 12:30 UTC460
    ///     DmRoomDefinition::default()
7ff8adafe9e4atagen2026-10-22 12:30 UTC461
    /// );
7ff8adafe9e4atagen2026-10-22 12:30 UTC462
    /// # async fn send_join_request() -> anyhow::Result<OwnedRoomId> { todo!() }
7ff8adafe9e4atagen2026-10-22 12:30 UTC463
    /// # async fn maybe_get_inviter(room_id: &RoomId) -> anyhow::Result<Option<OwnedUserId>> { todo!() }
7ff8adafe9e4atagen2026-10-22 12:30 UTC464
    /// # let room_id: &RoomId = todo!();
7ff8adafe9e4atagen2026-10-22 12:30 UTC465
    /// let maybe_inviter = maybe_get_inviter(room_id).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC466
    /// let room_id = send_join_request().await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC467
    /// let room = client.room_joined(&room_id, maybe_inviter).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC468
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC469
    /// assert_eq!(room.state(), RoomState::Joined);
7ff8adafe9e4atagen2026-10-22 12:30 UTC470
    /// # matrix_sdk_test::TestResult::Ok(()) };
7ff8adafe9e4atagen2026-10-22 12:30 UTC471
    /// ```
7ff8adafe9e4atagen2026-10-22 12:30 UTC472
    pub async fn room_joined(
7ff8adafe9e4atagen2026-10-22 12:30 UTC473
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC474
        room_id: &RoomId,
7ff8adafe9e4atagen2026-10-22 12:30 UTC475
        inviter: Option<OwnedUserId>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC476
    ) -> Result<Room> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC477
        let room = self.state_store.get_or_create_room(room_id, RoomState::Joined);
7ff8adafe9e4atagen2026-10-22 12:30 UTC478
7ff8adafe9e4atagen2026-10-22 12:30 UTC479
        // If the state isn't `RoomState::Joined` then this means that we knew about
7ff8adafe9e4atagen2026-10-22 12:30 UTC480
        // this room before. Let's modify the existing state now.
7ff8adafe9e4atagen2026-10-22 12:30 UTC481
        if room.state() != RoomState::Joined {
7ff8adafe9e4atagen2026-10-22 12:30 UTC482
            let store_guard = self.state_store_lock().lock().await;
7ff8adafe9e4atagen2026-10-22 12:30 UTC483
7ff8adafe9e4atagen2026-10-22 12:30 UTC484
            #[cfg(feature = "e2e-encryption")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC485
            {
7ff8adafe9e4atagen2026-10-22 12:30 UTC486
                // If our previous state was an invite and we're now in the joined state, this
7ff8adafe9e4atagen2026-10-22 12:30 UTC487
                // means that the user has explicitly accepted an invite. Let's
7ff8adafe9e4atagen2026-10-22 12:30 UTC488
                // remember some details about the invite.
7ff8adafe9e4atagen2026-10-22 12:30 UTC489
                //
7ff8adafe9e4atagen2026-10-22 12:30 UTC490
                // This is somewhat of a workaround for our lack of cryptographic membership.
7ff8adafe9e4atagen2026-10-22 12:30 UTC491
                // Later on we will decide if historic room keys should be accepted
7ff8adafe9e4atagen2026-10-22 12:30 UTC492
                // based on this info. If a user has accepted an invite and we receive a room
7ff8adafe9e4atagen2026-10-22 12:30 UTC493
                // key bundle shortly after, we might accept it. If we don't do
7ff8adafe9e4atagen2026-10-22 12:30 UTC494
                // this, the homeserver could trick us into accepting any historic room key
7ff8adafe9e4atagen2026-10-22 12:30 UTC495
                // bundle.
7ff8adafe9e4atagen2026-10-22 12:30 UTC496
                let previous_state = room.state();
7ff8adafe9e4atagen2026-10-22 12:30 UTC497
                if previous_state == RoomState::Invited
7ff8adafe9e4atagen2026-10-22 12:30 UTC498
                    && let Some(inviter) = inviter
7ff8adafe9e4atagen2026-10-22 12:30 UTC499
                    && let Some(olm_machine) = self.olm_machine().await.as_ref()
7ff8adafe9e4atagen2026-10-22 12:30 UTC500
                {
7ff8adafe9e4atagen2026-10-22 12:30 UTC501
                    olm_machine.store().store_room_pending_key_bundle(room_id, &inviter).await?
7ff8adafe9e4atagen2026-10-22 12:30 UTC502
                }
7ff8adafe9e4atagen2026-10-22 12:30 UTC503
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC504
            #[cfg(not(feature = "e2e-encryption"))]
7ff8adafe9e4atagen2026-10-22 12:30 UTC505
            {
7ff8adafe9e4atagen2026-10-22 12:30 UTC506
                // suppress unused argument warning
7ff8adafe9e4atagen2026-10-22 12:30 UTC507
                let _ = inviter;
7ff8adafe9e4atagen2026-10-22 12:30 UTC508
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC509
7ff8adafe9e4atagen2026-10-22 12:30 UTC510
            room.update_and_save_room_info_with_store_guard(&store_guard, |mut info| {
7ff8adafe9e4atagen2026-10-22 12:30 UTC511
                info.mark_as_joined();
7ff8adafe9e4atagen2026-10-22 12:30 UTC512
                info.mark_state_partially_synced();
7ff8adafe9e4atagen2026-10-22 12:30 UTC513
                info.mark_members_missing(); // the own member event changed
7ff8adafe9e4atagen2026-10-22 12:30 UTC514
                (info, RoomInfoNotableUpdateReasons::MEMBERSHIP)
7ff8adafe9e4atagen2026-10-22 12:30 UTC515
            })
7ff8adafe9e4atagen2026-10-22 12:30 UTC516
            .await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC517
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC518
7ff8adafe9e4atagen2026-10-22 12:30 UTC519
        Ok(room)
7ff8adafe9e4atagen2026-10-22 12:30 UTC520
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC521
7ff8adafe9e4atagen2026-10-22 12:30 UTC522
    /// User has left a room.
7ff8adafe9e4atagen2026-10-22 12:30 UTC523
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC524
    /// Update the internal and cached state accordingly.
7ff8adafe9e4atagen2026-10-22 12:30 UTC525
    pub async fn room_left(&self, room_id: &RoomId) -> Result<()> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC526
        let room = self.state_store.get_or_create_room(room_id, RoomState::Left);
7ff8adafe9e4atagen2026-10-22 12:30 UTC527
7ff8adafe9e4atagen2026-10-22 12:30 UTC528
        if room.state() != RoomState::Left {
7ff8adafe9e4atagen2026-10-22 12:30 UTC529
            let store_guard = self.state_store.lock().lock().await;
7ff8adafe9e4atagen2026-10-22 12:30 UTC530
7ff8adafe9e4atagen2026-10-22 12:30 UTC531
            // We are no longer joined to the room, so the invite acceptance details are no
7ff8adafe9e4atagen2026-10-22 12:30 UTC532
            // longer relevant.
7ff8adafe9e4atagen2026-10-22 12:30 UTC533
            #[cfg(feature = "e2e-encryption")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC534
            if let Some(olm_machine) = self.olm_machine().await.as_ref() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC535
                olm_machine.store().clear_room_pending_key_bundle(room_id).await?
7ff8adafe9e4atagen2026-10-22 12:30 UTC536
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC537
7ff8adafe9e4atagen2026-10-22 12:30 UTC538
            room.update_and_save_room_info_with_store_guard(&store_guard, |mut info| {
7ff8adafe9e4atagen2026-10-22 12:30 UTC539
                info.mark_as_left();
7ff8adafe9e4atagen2026-10-22 12:30 UTC540
                info.mark_state_partially_synced();
7ff8adafe9e4atagen2026-10-22 12:30 UTC541
                info.mark_members_missing(); // the own member event changed
7ff8adafe9e4atagen2026-10-22 12:30 UTC542
                (info, RoomInfoNotableUpdateReasons::MEMBERSHIP)
7ff8adafe9e4atagen2026-10-22 12:30 UTC543
            })
7ff8adafe9e4atagen2026-10-22 12:30 UTC544
            .await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC545
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC546
7ff8adafe9e4atagen2026-10-22 12:30 UTC547
        Ok(())
7ff8adafe9e4atagen2026-10-22 12:30 UTC548
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC549
7ff8adafe9e4atagen2026-10-22 12:30 UTC550
    /// Get a lock to the state store, with an exclusive access.
7ff8adafe9e4atagen2026-10-22 12:30 UTC551
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC552
    /// It doesn't give an access to the state store itself. It's rather a lock
7ff8adafe9e4atagen2026-10-22 12:30 UTC553
    /// to synchronise all accesses to the state store.
7ff8adafe9e4atagen2026-10-22 12:30 UTC554
    pub fn state_store_lock(&self) -> &Mutex<()> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC555
        self.state_store.lock()
7ff8adafe9e4atagen2026-10-22 12:30 UTC556
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC557
7ff8adafe9e4atagen2026-10-22 12:30 UTC558
    /// Receive a response from a sync call.
7ff8adafe9e4atagen2026-10-22 12:30 UTC559
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC560
    /// # Arguments
7ff8adafe9e4atagen2026-10-22 12:30 UTC561
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC562
    /// * `response` - The response that we received after a successful sync.
7ff8adafe9e4atagen2026-10-22 12:30 UTC563
    #[instrument(skip_all)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC564
    pub async fn receive_sync_response(
7ff8adafe9e4atagen2026-10-22 12:30 UTC565
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC566
        response: api::sync::sync_events::v3::Response,
7ff8adafe9e4atagen2026-10-22 12:30 UTC567
    ) -> Result<SyncResponse> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC568
        self.receive_sync_response_with_requested_required_states(
7ff8adafe9e4atagen2026-10-22 12:30 UTC569
            response,
7ff8adafe9e4atagen2026-10-22 12:30 UTC570
            &RequestedRequiredStates::default(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC571
        )
7ff8adafe9e4atagen2026-10-22 12:30 UTC572
        .await
7ff8adafe9e4atagen2026-10-22 12:30 UTC573
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC574
7ff8adafe9e4atagen2026-10-22 12:30 UTC575
    /// Receive a response from a sync call, with the requested required state
7ff8adafe9e4atagen2026-10-22 12:30 UTC576
    /// events.
7ff8adafe9e4atagen2026-10-22 12:30 UTC577
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC578
    /// # Arguments
7ff8adafe9e4atagen2026-10-22 12:30 UTC579
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC580
    /// * `response` - The response that we received after a successful sync.
7ff8adafe9e4atagen2026-10-22 12:30 UTC581
    /// * `requested_required_states` - The requested required state events.
7ff8adafe9e4atagen2026-10-22 12:30 UTC582
    pub async fn receive_sync_response_with_requested_required_states(
7ff8adafe9e4atagen2026-10-22 12:30 UTC583
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC584
        response: api::sync::sync_events::v3::Response,
7ff8adafe9e4atagen2026-10-22 12:30 UTC585
        requested_required_states: &RequestedRequiredStates,
7ff8adafe9e4atagen2026-10-22 12:30 UTC586
    ) -> Result<SyncResponse> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC587
        // The server might respond multiple times with the same sync token, in
7ff8adafe9e4atagen2026-10-22 12:30 UTC588
        // that case we already received this response and there's nothing to
7ff8adafe9e4atagen2026-10-22 12:30 UTC589
        // do.
7ff8adafe9e4atagen2026-10-22 12:30 UTC590
        if self.state_store.sync_token.read().await.as_ref() == Some(&response.next_batch) {
7ff8adafe9e4atagen2026-10-22 12:30 UTC591
            info!("Got the same sync response twice");
7ff8adafe9e4atagen2026-10-22 12:30 UTC592
            return Ok(SyncResponse::default());
7ff8adafe9e4atagen2026-10-22 12:30 UTC593
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC594
7ff8adafe9e4atagen2026-10-22 12:30 UTC595
        let now = if enabled!(Level::INFO) { Some(Instant::now()) } else { None };
7ff8adafe9e4atagen2026-10-22 12:30 UTC596
7ff8adafe9e4atagen2026-10-22 12:30 UTC597
        // Acquire the state store lock and hold on to it while processing
7ff8adafe9e4atagen2026-10-22 12:30 UTC598
        // the sync response below.
7ff8adafe9e4atagen2026-10-22 12:30 UTC599
        let state_store_guard = self.state_store_lock().lock().await;
7ff8adafe9e4atagen2026-10-22 12:30 UTC600
7ff8adafe9e4atagen2026-10-22 12:30 UTC601
        let user_id = self
7ff8adafe9e4atagen2026-10-22 12:30 UTC602
            .session_meta()
7ff8adafe9e4atagen2026-10-22 12:30 UTC603
            .expect("Sync shouldn't run without an authenticated user")
7ff8adafe9e4atagen2026-10-22 12:30 UTC604
            .user_id
7ff8adafe9e4atagen2026-10-22 12:30 UTC605
            .to_owned();
7ff8adafe9e4atagen2026-10-22 12:30 UTC606
7ff8adafe9e4atagen2026-10-22 12:30 UTC607
        #[cfg(feature = "e2e-encryption")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC608
        let olm_machine = self.olm_machine().await;
7ff8adafe9e4atagen2026-10-22 12:30 UTC609
7ff8adafe9e4atagen2026-10-22 12:30 UTC610
        let mut context = Context::new(StateChanges::new(response.next_batch.clone()));
7ff8adafe9e4atagen2026-10-22 12:30 UTC611
7ff8adafe9e4atagen2026-10-22 12:30 UTC612
        #[cfg(feature = "e2e-encryption")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC613
        let processors::e2ee::to_device::Output { processed_to_device_events: to_device } =
7ff8adafe9e4atagen2026-10-22 12:30 UTC614
            processors::e2ee::to_device::from_sync_v2(
7ff8adafe9e4atagen2026-10-22 12:30 UTC615
                &response,
7ff8adafe9e4atagen2026-10-22 12:30 UTC616
                olm_machine.as_ref(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC617
                &self.decryption_settings,
7ff8adafe9e4atagen2026-10-22 12:30 UTC618
            )
7ff8adafe9e4atagen2026-10-22 12:30 UTC619
            .await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC620
7ff8adafe9e4atagen2026-10-22 12:30 UTC621
        #[cfg(not(feature = "e2e-encryption"))]
7ff8adafe9e4atagen2026-10-22 12:30 UTC622
        let to_device = response
7ff8adafe9e4atagen2026-10-22 12:30 UTC623
            .to_device
7ff8adafe9e4atagen2026-10-22 12:30 UTC624
            .events
7ff8adafe9e4atagen2026-10-22 12:30 UTC625
            .into_iter()
7ff8adafe9e4atagen2026-10-22 12:30 UTC626
            .map(|raw| {
7ff8adafe9e4atagen2026-10-22 12:30 UTC627
                use matrix_sdk_common::deserialized_responses::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC628
                    ProcessedToDeviceEvent, ToDeviceUnableToDecryptInfo,
7ff8adafe9e4atagen2026-10-22 12:30 UTC629
                    ToDeviceUnableToDecryptReason,
7ff8adafe9e4atagen2026-10-22 12:30 UTC630
                };
7ff8adafe9e4atagen2026-10-22 12:30 UTC631
7ff8adafe9e4atagen2026-10-22 12:30 UTC632
                if let Ok(Some(event_type)) = raw.get_field::<String>("type") {
7ff8adafe9e4atagen2026-10-22 12:30 UTC633
                    if event_type == "m.room.encrypted" {
7ff8adafe9e4atagen2026-10-22 12:30 UTC634
                        ProcessedToDeviceEvent::UnableToDecrypt {
7ff8adafe9e4atagen2026-10-22 12:30 UTC635
                            encrypted_event: raw,
7ff8adafe9e4atagen2026-10-22 12:30 UTC636
                            utd_info: ToDeviceUnableToDecryptInfo {
7ff8adafe9e4atagen2026-10-22 12:30 UTC637
                                reason: ToDeviceUnableToDecryptReason::EncryptionIsDisabled,
7ff8adafe9e4atagen2026-10-22 12:30 UTC638
                            },
7ff8adafe9e4atagen2026-10-22 12:30 UTC639
                        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC640
                    } else {
7ff8adafe9e4atagen2026-10-22 12:30 UTC641
                        ProcessedToDeviceEvent::PlainText(raw)
7ff8adafe9e4atagen2026-10-22 12:30 UTC642
                    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC643
                } else {
7ff8adafe9e4atagen2026-10-22 12:30 UTC644
                    // Exclude events with no type
7ff8adafe9e4atagen2026-10-22 12:30 UTC645
                    ProcessedToDeviceEvent::Invalid(raw)
7ff8adafe9e4atagen2026-10-22 12:30 UTC646
                }
7ff8adafe9e4atagen2026-10-22 12:30 UTC647
            })
7ff8adafe9e4atagen2026-10-22 12:30 UTC648
            .collect();
7ff8adafe9e4atagen2026-10-22 12:30 UTC649
7ff8adafe9e4atagen2026-10-22 12:30 UTC650
        let mut ambiguity_cache = AmbiguityCache::new(self.state_store.inner.clone());
7ff8adafe9e4atagen2026-10-22 12:30 UTC651
        let mut avatar_cache = AvatarCache::new(self.state_store.inner.clone());
7ff8adafe9e4atagen2026-10-22 12:30 UTC652
7ff8adafe9e4atagen2026-10-22 12:30 UTC653
        let global_account_data_processor =
7ff8adafe9e4atagen2026-10-22 12:30 UTC654
            processors::account_data::global(&response.account_data.events);
7ff8adafe9e4atagen2026-10-22 12:30 UTC655
7ff8adafe9e4atagen2026-10-22 12:30 UTC656
        let push_rules = self.get_push_rules(&global_account_data_processor).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC657
7ff8adafe9e4atagen2026-10-22 12:30 UTC658
        let mut room_updates = RoomUpdates::default();
7ff8adafe9e4atagen2026-10-22 12:30 UTC659
        let mut notifications = Default::default();
7ff8adafe9e4atagen2026-10-22 12:30 UTC660
7ff8adafe9e4atagen2026-10-22 12:30 UTC661
        let mut updated_members_in_room: BTreeMap<OwnedRoomId, BTreeSet<OwnedUserId>> =
7ff8adafe9e4atagen2026-10-22 12:30 UTC662
            BTreeMap::new();
7ff8adafe9e4atagen2026-10-22 12:30 UTC663
7ff8adafe9e4atagen2026-10-22 12:30 UTC664
        #[cfg(feature = "e2e-encryption")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC665
        let e2ee_context = processors::e2ee::E2EE::new(
7ff8adafe9e4atagen2026-10-22 12:30 UTC666
            olm_machine.as_ref(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC667
            &self.decryption_settings,
7ff8adafe9e4atagen2026-10-22 12:30 UTC668
            self.handle_verification_events,
7ff8adafe9e4atagen2026-10-22 12:30 UTC669
        );
7ff8adafe9e4atagen2026-10-22 12:30 UTC670
7ff8adafe9e4atagen2026-10-22 12:30 UTC671
        for (room_id, joined_room) in response.rooms.join {
7ff8adafe9e4atagen2026-10-22 12:30 UTC672
            let joined_room_update = processors::room::sync_v2::update_joined_room(
7ff8adafe9e4atagen2026-10-22 12:30 UTC673
                &mut context,
7ff8adafe9e4atagen2026-10-22 12:30 UTC674
                processors::room::RoomCreationData::new(
7ff8adafe9e4atagen2026-10-22 12:30 UTC675
                    &room_id,
7ff8adafe9e4atagen2026-10-22 12:30 UTC676
                    requested_required_states,
7ff8adafe9e4atagen2026-10-22 12:30 UTC677
                    &mut ambiguity_cache,
7ff8adafe9e4atagen2026-10-22 12:30 UTC678
                    &mut avatar_cache,
7ff8adafe9e4atagen2026-10-22 12:30 UTC679
                ),
7ff8adafe9e4atagen2026-10-22 12:30 UTC680
                joined_room,
7ff8adafe9e4atagen2026-10-22 12:30 UTC681
                &mut updated_members_in_room,
7ff8adafe9e4atagen2026-10-22 12:30 UTC682
                processors::notification::Notification::new(
7ff8adafe9e4atagen2026-10-22 12:30 UTC683
                    &push_rules,
7ff8adafe9e4atagen2026-10-22 12:30 UTC684
                    &mut notifications,
7ff8adafe9e4atagen2026-10-22 12:30 UTC685
                    &self.state_store,
7ff8adafe9e4atagen2026-10-22 12:30 UTC686
                ),
7ff8adafe9e4atagen2026-10-22 12:30 UTC687
                #[cfg(feature = "e2e-encryption")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC688
                &e2ee_context,
7ff8adafe9e4atagen2026-10-22 12:30 UTC689
            )
7ff8adafe9e4atagen2026-10-22 12:30 UTC690
            .await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC691
7ff8adafe9e4atagen2026-10-22 12:30 UTC692
            room_updates.joined.insert(room_id, joined_room_update);
7ff8adafe9e4atagen2026-10-22 12:30 UTC693
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC694
7ff8adafe9e4atagen2026-10-22 12:30 UTC695
        for (room_id, left_room) in response.rooms.leave {
7ff8adafe9e4atagen2026-10-22 12:30 UTC696
            let left_room_update = processors::room::sync_v2::update_left_room(
7ff8adafe9e4atagen2026-10-22 12:30 UTC697
                &mut context,
7ff8adafe9e4atagen2026-10-22 12:30 UTC698
                processors::room::RoomCreationData::new(
7ff8adafe9e4atagen2026-10-22 12:30 UTC699
                    &room_id,
7ff8adafe9e4atagen2026-10-22 12:30 UTC700
                    requested_required_states,
7ff8adafe9e4atagen2026-10-22 12:30 UTC701
                    &mut ambiguity_cache,
7ff8adafe9e4atagen2026-10-22 12:30 UTC702
                    &mut avatar_cache,
7ff8adafe9e4atagen2026-10-22 12:30 UTC703
                ),
7ff8adafe9e4atagen2026-10-22 12:30 UTC704
                left_room,
7ff8adafe9e4atagen2026-10-22 12:30 UTC705
                processors::notification::Notification::new(
7ff8adafe9e4atagen2026-10-22 12:30 UTC706
                    &push_rules,
7ff8adafe9e4atagen2026-10-22 12:30 UTC707
                    &mut notifications,
7ff8adafe9e4atagen2026-10-22 12:30 UTC708
                    &self.state_store,
7ff8adafe9e4atagen2026-10-22 12:30 UTC709
                ),
7ff8adafe9e4atagen2026-10-22 12:30 UTC710
                #[cfg(feature = "e2e-encryption")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC711
                &e2ee_context,
7ff8adafe9e4atagen2026-10-22 12:30 UTC712
            )
7ff8adafe9e4atagen2026-10-22 12:30 UTC713
            .await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC714
7ff8adafe9e4atagen2026-10-22 12:30 UTC715
            room_updates.left.insert(room_id, left_room_update);
7ff8adafe9e4atagen2026-10-22 12:30 UTC716
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC717
7ff8adafe9e4atagen2026-10-22 12:30 UTC718
        for (room_id, invited_room) in response.rooms.invite {
7ff8adafe9e4atagen2026-10-22 12:30 UTC719
            let invited_room_update = processors::room::sync_v2::update_invited_room(
7ff8adafe9e4atagen2026-10-22 12:30 UTC720
                &mut context,
7ff8adafe9e4atagen2026-10-22 12:30 UTC721
                &room_id,
7ff8adafe9e4atagen2026-10-22 12:30 UTC722
                &user_id,
7ff8adafe9e4atagen2026-10-22 12:30 UTC723
                invited_room,
7ff8adafe9e4atagen2026-10-22 12:30 UTC724
                processors::notification::Notification::new(
7ff8adafe9e4atagen2026-10-22 12:30 UTC725
                    &push_rules,
7ff8adafe9e4atagen2026-10-22 12:30 UTC726
                    &mut notifications,
7ff8adafe9e4atagen2026-10-22 12:30 UTC727
                    &self.state_store,
7ff8adafe9e4atagen2026-10-22 12:30 UTC728
                ),
7ff8adafe9e4atagen2026-10-22 12:30 UTC729
                #[cfg(feature = "e2e-encryption")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC730
                &e2ee_context,
7ff8adafe9e4atagen2026-10-22 12:30 UTC731
            )
7ff8adafe9e4atagen2026-10-22 12:30 UTC732
            .await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC733
7ff8adafe9e4atagen2026-10-22 12:30 UTC734
            room_updates.invited.insert(room_id, invited_room_update);
7ff8adafe9e4atagen2026-10-22 12:30 UTC735
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC736
7ff8adafe9e4atagen2026-10-22 12:30 UTC737
        for (room_id, knocked_room) in response.rooms.knock {
7ff8adafe9e4atagen2026-10-22 12:30 UTC738
            let knocked_room_update = processors::room::sync_v2::update_knocked_room(
7ff8adafe9e4atagen2026-10-22 12:30 UTC739
                &mut context,
7ff8adafe9e4atagen2026-10-22 12:30 UTC740
                &room_id,
7ff8adafe9e4atagen2026-10-22 12:30 UTC741
                &user_id,
7ff8adafe9e4atagen2026-10-22 12:30 UTC742
                knocked_room,
7ff8adafe9e4atagen2026-10-22 12:30 UTC743
                processors::notification::Notification::new(
7ff8adafe9e4atagen2026-10-22 12:30 UTC744
                    &push_rules,
7ff8adafe9e4atagen2026-10-22 12:30 UTC745
                    &mut notifications,
7ff8adafe9e4atagen2026-10-22 12:30 UTC746
                    &self.state_store,
7ff8adafe9e4atagen2026-10-22 12:30 UTC747
                ),
7ff8adafe9e4atagen2026-10-22 12:30 UTC748
                #[cfg(feature = "e2e-encryption")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC749
                &e2ee_context,
7ff8adafe9e4atagen2026-10-22 12:30 UTC750
            )
7ff8adafe9e4atagen2026-10-22 12:30 UTC751
            .await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC752
7ff8adafe9e4atagen2026-10-22 12:30 UTC753
            room_updates.knocked.insert(room_id, knocked_room_update);
7ff8adafe9e4atagen2026-10-22 12:30 UTC754
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC755
7ff8adafe9e4atagen2026-10-22 12:30 UTC756
        global_account_data_processor.apply(&mut context, &self.state_store).await;
7ff8adafe9e4atagen2026-10-22 12:30 UTC757
7ff8adafe9e4atagen2026-10-22 12:30 UTC758
        context.state_changes.presence = response
7ff8adafe9e4atagen2026-10-22 12:30 UTC759
            .presence
7ff8adafe9e4atagen2026-10-22 12:30 UTC760
            .events
7ff8adafe9e4atagen2026-10-22 12:30 UTC761
            .iter()
7ff8adafe9e4atagen2026-10-22 12:30 UTC762
            .filter_map(|e| {
7ff8adafe9e4atagen2026-10-22 12:30 UTC763
                let event = e.deserialize().ok()?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC764
                Some((event.sender, e.clone()))
7ff8adafe9e4atagen2026-10-22 12:30 UTC765
            })
7ff8adafe9e4atagen2026-10-22 12:30 UTC766
            .collect();
7ff8adafe9e4atagen2026-10-22 12:30 UTC767
7ff8adafe9e4atagen2026-10-22 12:30 UTC768
        context.state_changes.ambiguity_maps = ambiguity_cache.cache;
7ff8adafe9e4atagen2026-10-22 12:30 UTC769
7ff8adafe9e4atagen2026-10-22 12:30 UTC770
        processors::changes::save_and_apply(
7ff8adafe9e4atagen2026-10-22 12:30 UTC771
            context,
7ff8adafe9e4atagen2026-10-22 12:30 UTC772
            &self.state_store,
7ff8adafe9e4atagen2026-10-22 12:30 UTC773
            &state_store_guard,
7ff8adafe9e4atagen2026-10-22 12:30 UTC774
            &self.ignore_user_list_changes,
7ff8adafe9e4atagen2026-10-22 12:30 UTC775
            Some(response.next_batch.clone()),
7ff8adafe9e4atagen2026-10-22 12:30 UTC776
        )
7ff8adafe9e4atagen2026-10-22 12:30 UTC777
        .await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC778
7ff8adafe9e4atagen2026-10-22 12:30 UTC779
        let mut context = Context::default();
7ff8adafe9e4atagen2026-10-22 12:30 UTC780
7ff8adafe9e4atagen2026-10-22 12:30 UTC781
        // Now that all the rooms information have been saved, update the display name
7ff8adafe9e4atagen2026-10-22 12:30 UTC782
        // of the updated rooms (which relies on information stored in the database).
7ff8adafe9e4atagen2026-10-22 12:30 UTC783
        processors::room::display_name::update_for_rooms(
7ff8adafe9e4atagen2026-10-22 12:30 UTC784
            &mut context,
7ff8adafe9e4atagen2026-10-22 12:30 UTC785
            &room_updates,
7ff8adafe9e4atagen2026-10-22 12:30 UTC786
            &self.state_store,
7ff8adafe9e4atagen2026-10-22 12:30 UTC787
        )
7ff8adafe9e4atagen2026-10-22 12:30 UTC788
        .await;
7ff8adafe9e4atagen2026-10-22 12:30 UTC789
7ff8adafe9e4atagen2026-10-22 12:30 UTC790
        // Save the new display name updates if any.
7ff8adafe9e4atagen2026-10-22 12:30 UTC791
        processors::changes::save_only(context, &self.state_store, &state_store_guard).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC792
7ff8adafe9e4atagen2026-10-22 12:30 UTC793
        for (room_id, member_ids) in updated_members_in_room {
7ff8adafe9e4atagen2026-10-22 12:30 UTC794
            if let Some(room) = self.get_room(&room_id) {
7ff8adafe9e4atagen2026-10-22 12:30 UTC795
                let _ =
7ff8adafe9e4atagen2026-10-22 12:30 UTC796
                    room.room_member_updates_sender.send(RoomMembersUpdate::Partial(member_ids));
7ff8adafe9e4atagen2026-10-22 12:30 UTC797
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC798
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC799
7ff8adafe9e4atagen2026-10-22 12:30 UTC800
        // Release the state store lock
7ff8adafe9e4atagen2026-10-22 12:30 UTC801
        drop(state_store_guard);
7ff8adafe9e4atagen2026-10-22 12:30 UTC802
7ff8adafe9e4atagen2026-10-22 12:30 UTC803
        if enabled!(Level::INFO) {
7ff8adafe9e4atagen2026-10-22 12:30 UTC804
            info!("Processed a sync response in {:?}", now.map(|now| now.elapsed()));
7ff8adafe9e4atagen2026-10-22 12:30 UTC805
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC806
7ff8adafe9e4atagen2026-10-22 12:30 UTC807
        let response = SyncResponse {
7ff8adafe9e4atagen2026-10-22 12:30 UTC808
            rooms: room_updates,
7ff8adafe9e4atagen2026-10-22 12:30 UTC809
            presence: response.presence.events,
7ff8adafe9e4atagen2026-10-22 12:30 UTC810
            account_data: response.account_data.events,
7ff8adafe9e4atagen2026-10-22 12:30 UTC811
            to_device,
7ff8adafe9e4atagen2026-10-22 12:30 UTC812
            notifications,
7ff8adafe9e4atagen2026-10-22 12:30 UTC813
        };
7ff8adafe9e4atagen2026-10-22 12:30 UTC814
7ff8adafe9e4atagen2026-10-22 12:30 UTC815
        Ok(response)
7ff8adafe9e4atagen2026-10-22 12:30 UTC816
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC817
7ff8adafe9e4atagen2026-10-22 12:30 UTC818
    /// Receive a get member events response and convert it to a deserialized
7ff8adafe9e4atagen2026-10-22 12:30 UTC819
    /// `MembersResponse`
7ff8adafe9e4atagen2026-10-22 12:30 UTC820
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC821
    /// This client-server request must be made without filters to make sure all
7ff8adafe9e4atagen2026-10-22 12:30 UTC822
    /// members are received. Otherwise, an error is returned.
7ff8adafe9e4atagen2026-10-22 12:30 UTC823
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC824
    /// # Arguments
7ff8adafe9e4atagen2026-10-22 12:30 UTC825
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC826
    /// * `room_id` - The room id this response belongs to.
7ff8adafe9e4atagen2026-10-22 12:30 UTC827
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC828
    /// * `response` - The raw response that was received from the server.
7ff8adafe9e4atagen2026-10-22 12:30 UTC829
    #[instrument(skip_all, fields(?room_id))]
7ff8adafe9e4atagen2026-10-22 12:30 UTC830
    pub async fn receive_all_members(
7ff8adafe9e4atagen2026-10-22 12:30 UTC831
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC832
        room_id: &RoomId,
7ff8adafe9e4atagen2026-10-22 12:30 UTC833
        request: &api::membership::get_member_events::v3::Request,
7ff8adafe9e4atagen2026-10-22 12:30 UTC834
        response: &api::membership::get_member_events::v3::Response,
7ff8adafe9e4atagen2026-10-22 12:30 UTC835
    ) -> Result<()> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC836
        if request.membership.is_some() || request.not_membership.is_some() || request.at.is_some()
7ff8adafe9e4atagen2026-10-22 12:30 UTC837
        {
7ff8adafe9e4atagen2026-10-22 12:30 UTC838
            // This function assumes all members are loaded at once to optimise how display
7ff8adafe9e4atagen2026-10-22 12:30 UTC839
            // name disambiguation works. Using it with partial member list results
7ff8adafe9e4atagen2026-10-22 12:30 UTC840
            // would produce incorrect disambiguated display name entries
7ff8adafe9e4atagen2026-10-22 12:30 UTC841
            return Err(Error::InvalidReceiveMembersParameters);
7ff8adafe9e4atagen2026-10-22 12:30 UTC842
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC843
7ff8adafe9e4atagen2026-10-22 12:30 UTC844
        let Some(room) = self.state_store.room(room_id) else {
7ff8adafe9e4atagen2026-10-22 12:30 UTC845
            // The room is unknown to us: leave early.
7ff8adafe9e4atagen2026-10-22 12:30 UTC846
            return Ok(());
7ff8adafe9e4atagen2026-10-22 12:30 UTC847
        };
7ff8adafe9e4atagen2026-10-22 12:30 UTC848
7ff8adafe9e4atagen2026-10-22 12:30 UTC849
        let mut chunk = Vec::with_capacity(response.chunk.len());
7ff8adafe9e4atagen2026-10-22 12:30 UTC850
        let mut context = Context::default();
7ff8adafe9e4atagen2026-10-22 12:30 UTC851
7ff8adafe9e4atagen2026-10-22 12:30 UTC852
        #[cfg(feature = "e2e-encryption")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC853
        let mut user_ids = BTreeSet::new();
7ff8adafe9e4atagen2026-10-22 12:30 UTC854
7ff8adafe9e4atagen2026-10-22 12:30 UTC855
        let mut ambiguity_map: HashMap<DisplayName, BTreeSet<OwnedUserId>> = Default::default();
7ff8adafe9e4atagen2026-10-22 12:30 UTC856
7ff8adafe9e4atagen2026-10-22 12:30 UTC857
        for raw_event in &response.chunk {
7ff8adafe9e4atagen2026-10-22 12:30 UTC858
            let member = match raw_event.deserialize() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC859
                Ok(ev) => ev,
7ff8adafe9e4atagen2026-10-22 12:30 UTC860
                Err(e) => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC861
                    let event_id: Option<String> = raw_event.get_field("event_id").ok().flatten();
7ff8adafe9e4atagen2026-10-22 12:30 UTC862
                    debug!(event_id, "Failed to deserialize member event: {e}");
7ff8adafe9e4atagen2026-10-22 12:30 UTC863
                    continue;
7ff8adafe9e4atagen2026-10-22 12:30 UTC864
                }
7ff8adafe9e4atagen2026-10-22 12:30 UTC865
            };
7ff8adafe9e4atagen2026-10-22 12:30 UTC866
7ff8adafe9e4atagen2026-10-22 12:30 UTC867
            // TODO: All the actions in this loop used to be done only when the membership
7ff8adafe9e4atagen2026-10-22 12:30 UTC868
            // event was not in the store before. This was changed with the new room API,
7ff8adafe9e4atagen2026-10-22 12:30 UTC869
            // because e.g. leaving a room makes members events outdated and they need to be
7ff8adafe9e4atagen2026-10-22 12:30 UTC870
            // fetched by `members`. Therefore, they need to be overwritten here, even
7ff8adafe9e4atagen2026-10-22 12:30 UTC871
            // if they exist.
7ff8adafe9e4atagen2026-10-22 12:30 UTC872
            // However, this makes a new problem occur where setting the member events here
7ff8adafe9e4atagen2026-10-22 12:30 UTC873
            // potentially races with the sync.
7ff8adafe9e4atagen2026-10-22 12:30 UTC874
            // See <https://github.com/matrix-org/matrix-rust-sdk/issues/1205>.
7ff8adafe9e4atagen2026-10-22 12:30 UTC875
7ff8adafe9e4atagen2026-10-22 12:30 UTC876
            #[cfg(feature = "e2e-encryption")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC877
            match member.membership() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC878
                MembershipState::Join | MembershipState::Invite => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC879
                    user_ids.insert(member.state_key().to_owned());
7ff8adafe9e4atagen2026-10-22 12:30 UTC880
                }
7ff8adafe9e4atagen2026-10-22 12:30 UTC881
                _ => (),
7ff8adafe9e4atagen2026-10-22 12:30 UTC882
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC883
7ff8adafe9e4atagen2026-10-22 12:30 UTC884
            if let StateEvent::Original(e) = &member
7ff8adafe9e4atagen2026-10-22 12:30 UTC885
                && let Some(d) = &e.content.displayname
7ff8adafe9e4atagen2026-10-22 12:30 UTC886
            {
7ff8adafe9e4atagen2026-10-22 12:30 UTC887
                let display_name = DisplayName::new(d);
7ff8adafe9e4atagen2026-10-22 12:30 UTC888
                ambiguity_map.entry(display_name).or_default().insert(member.state_key().clone());
7ff8adafe9e4atagen2026-10-22 12:30 UTC889
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC890
7ff8adafe9e4atagen2026-10-22 12:30 UTC891
            let sync_member: SyncRoomMemberEvent = member.clone().into();
7ff8adafe9e4atagen2026-10-22 12:30 UTC892
            processors::profiles::upsert_or_delete(&mut context, room_id, &sync_member);
7ff8adafe9e4atagen2026-10-22 12:30 UTC893
7ff8adafe9e4atagen2026-10-22 12:30 UTC894
            context
7ff8adafe9e4atagen2026-10-22 12:30 UTC895
                .state_changes
7ff8adafe9e4atagen2026-10-22 12:30 UTC896
                .state
7ff8adafe9e4atagen2026-10-22 12:30 UTC897
                .entry(room_id.to_owned())
7ff8adafe9e4atagen2026-10-22 12:30 UTC898
                .or_default()
7ff8adafe9e4atagen2026-10-22 12:30 UTC899
                .entry(member.event_type())
7ff8adafe9e4atagen2026-10-22 12:30 UTC900
                .or_default()
7ff8adafe9e4atagen2026-10-22 12:30 UTC901
                .insert(member.state_key().to_string(), raw_event.clone().cast());
7ff8adafe9e4atagen2026-10-22 12:30 UTC902
            chunk.push(member);
7ff8adafe9e4atagen2026-10-22 12:30 UTC903
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC904
7ff8adafe9e4atagen2026-10-22 12:30 UTC905
        #[cfg(feature = "e2e-encryption")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC906
        processors::e2ee::tracked_users::update(
7ff8adafe9e4atagen2026-10-22 12:30 UTC907
            self.olm_machine().await.as_ref(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC908
            room.encryption_state(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC909
            &user_ids,
7ff8adafe9e4atagen2026-10-22 12:30 UTC910
        )
7ff8adafe9e4atagen2026-10-22 12:30 UTC911
        .await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC912
7ff8adafe9e4atagen2026-10-22 12:30 UTC913
        context.state_changes.ambiguity_maps.insert(room_id.to_owned(), ambiguity_map);
7ff8adafe9e4atagen2026-10-22 12:30 UTC914
7ff8adafe9e4atagen2026-10-22 12:30 UTC915
        {
7ff8adafe9e4atagen2026-10-22 12:30 UTC916
            let state_store_guard = self.state_store_lock().lock().await;
7ff8adafe9e4atagen2026-10-22 12:30 UTC917
7ff8adafe9e4atagen2026-10-22 12:30 UTC918
            let mut room_info = room.clone_info();
7ff8adafe9e4atagen2026-10-22 12:30 UTC919
            room_info.mark_members_synced();
7ff8adafe9e4atagen2026-10-22 12:30 UTC920
            context.state_changes.add_room(room_info);
7ff8adafe9e4atagen2026-10-22 12:30 UTC921
7ff8adafe9e4atagen2026-10-22 12:30 UTC922
            processors::changes::save_and_apply(
7ff8adafe9e4atagen2026-10-22 12:30 UTC923
                context,
7ff8adafe9e4atagen2026-10-22 12:30 UTC924
                &self.state_store,
7ff8adafe9e4atagen2026-10-22 12:30 UTC925
                &state_store_guard,
7ff8adafe9e4atagen2026-10-22 12:30 UTC926
                &self.ignore_user_list_changes,
7ff8adafe9e4atagen2026-10-22 12:30 UTC927
                None,
7ff8adafe9e4atagen2026-10-22 12:30 UTC928
            )
7ff8adafe9e4atagen2026-10-22 12:30 UTC929
            .await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC930
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC931
7ff8adafe9e4atagen2026-10-22 12:30 UTC932
        let _ = room.room_member_updates_sender.send(RoomMembersUpdate::FullReload);
7ff8adafe9e4atagen2026-10-22 12:30 UTC933
7ff8adafe9e4atagen2026-10-22 12:30 UTC934
        #[cfg(feature = "e2e-encryption")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC935
        if let Some(olm) = self.olm_machine().await.as_ref() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC936
            // With the introduction of MSC4268, it is no longer sufficient to check for
7ff8adafe9e4atagen2026-10-22 12:30 UTC937
            // changes to session recipients when we send a message, since we may miss
7ff8adafe9e4atagen2026-10-22 12:30 UTC938
            // join/leave pairs in our view of the room state. Instead, we should rotate
7ff8adafe9e4atagen2026-10-22 12:30 UTC939
            // the room key whenever we fully reload the member list as a precaution.
7ff8adafe9e4atagen2026-10-22 12:30 UTC940
            tracing::debug!("Rotating room key due to full member list reload");
7ff8adafe9e4atagen2026-10-22 12:30 UTC941
            if let Err(e) = olm.discard_room_key(room_id).await {
7ff8adafe9e4atagen2026-10-22 12:30 UTC942
                tracing::warn!("Error discarding room key: {e:?}");
7ff8adafe9e4atagen2026-10-22 12:30 UTC943
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC944
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC945
7ff8adafe9e4atagen2026-10-22 12:30 UTC946
        Ok(())
7ff8adafe9e4atagen2026-10-22 12:30 UTC947
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC948
7ff8adafe9e4atagen2026-10-22 12:30 UTC949
    /// Receive a successful filter upload response, the filter id will be
7ff8adafe9e4atagen2026-10-22 12:30 UTC950
    /// stored under the given name in the store.
7ff8adafe9e4atagen2026-10-22 12:30 UTC951
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC952
    /// The filter id can later be retrieved with the [`get_filter`] method.
7ff8adafe9e4atagen2026-10-22 12:30 UTC953
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC954
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC955
    /// # Arguments
7ff8adafe9e4atagen2026-10-22 12:30 UTC956
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC957
    /// * `filter_name` - The name that should be used to persist the filter id
7ff8adafe9e4atagen2026-10-22 12:30 UTC958
    ///   in the store.
7ff8adafe9e4atagen2026-10-22 12:30 UTC959
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC960
    /// * `response` - The successful filter upload response containing the
7ff8adafe9e4atagen2026-10-22 12:30 UTC961
    ///   filter id.
7ff8adafe9e4atagen2026-10-22 12:30 UTC962
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC963
    /// [`get_filter`]: #method.get_filter
7ff8adafe9e4atagen2026-10-22 12:30 UTC964
    pub async fn receive_filter_upload(
7ff8adafe9e4atagen2026-10-22 12:30 UTC965
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC966
        filter_name: &str,
7ff8adafe9e4atagen2026-10-22 12:30 UTC967
        response: &api::filter::create_filter::v3::Response,
7ff8adafe9e4atagen2026-10-22 12:30 UTC968
    ) -> Result<()> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC969
        Ok(self
7ff8adafe9e4atagen2026-10-22 12:30 UTC970
            .state_store
7ff8adafe9e4atagen2026-10-22 12:30 UTC971
            .set_kv_data(
7ff8adafe9e4atagen2026-10-22 12:30 UTC972
                StateStoreDataKey::Filter(filter_name),
7ff8adafe9e4atagen2026-10-22 12:30 UTC973
                StateStoreDataValue::Filter(response.filter_id.clone()),
7ff8adafe9e4atagen2026-10-22 12:30 UTC974
            )
7ff8adafe9e4atagen2026-10-22 12:30 UTC975
            .await?)
7ff8adafe9e4atagen2026-10-22 12:30 UTC976
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC977
7ff8adafe9e4atagen2026-10-22 12:30 UTC978
    /// Get the filter id of a previously uploaded filter.
7ff8adafe9e4atagen2026-10-22 12:30 UTC979
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC980
    /// *Note*: A filter will first need to be uploaded and persisted using
7ff8adafe9e4atagen2026-10-22 12:30 UTC981
    /// [`receive_filter_upload`].
7ff8adafe9e4atagen2026-10-22 12:30 UTC982
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC983
    /// # Arguments
7ff8adafe9e4atagen2026-10-22 12:30 UTC984
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC985
    /// * `filter_name` - The name of the filter that was previously used to
7ff8adafe9e4atagen2026-10-22 12:30 UTC986
    ///   persist the filter.
7ff8adafe9e4atagen2026-10-22 12:30 UTC987
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC988
    /// [`receive_filter_upload`]: #method.receive_filter_upload
7ff8adafe9e4atagen2026-10-22 12:30 UTC989
    pub async fn get_filter(&self, filter_name: &str) -> StoreResult<Option<String>> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC990
        let filter = self
7ff8adafe9e4atagen2026-10-22 12:30 UTC991
            .state_store
7ff8adafe9e4atagen2026-10-22 12:30 UTC992
            .get_kv_data(StateStoreDataKey::Filter(filter_name))
7ff8adafe9e4atagen2026-10-22 12:30 UTC993
            .await?
7ff8adafe9e4atagen2026-10-22 12:30 UTC994
            .map(|d| d.into_filter().expect("State store data not a filter"));
7ff8adafe9e4atagen2026-10-22 12:30 UTC995
7ff8adafe9e4atagen2026-10-22 12:30 UTC996
        Ok(filter)
7ff8adafe9e4atagen2026-10-22 12:30 UTC997
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC998
7ff8adafe9e4atagen2026-10-22 12:30 UTC999
    /// Get a to-device request that will share a room key with users in a room.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1000
    #[cfg(feature = "e2e-encryption")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC1001
    pub async fn share_room_key(&self, room_id: &RoomId) -> Result<Vec<Arc<ToDeviceRequest>>> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1002
        match self.olm_machine().await.as_ref() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1003
            Some(o) => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1004
                let Some(room) = self.get_room(room_id) else {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1005
                    return Err(Error::InsufficientData);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1006
                };
7ff8adafe9e4atagen2026-10-22 12:30 UTC1007
7ff8adafe9e4atagen2026-10-22 12:30 UTC1008
                let history_visibility = room.history_visibility_or_default();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1009
                let Some(room_encryption_event) = room.encryption_settings() else {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1010
                    return Err(Error::EncryptionNotEnabled);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1011
                };
7ff8adafe9e4atagen2026-10-22 12:30 UTC1012
7ff8adafe9e4atagen2026-10-22 12:30 UTC1013
                // Don't share the group session with members that are invited
7ff8adafe9e4atagen2026-10-22 12:30 UTC1014
                // if the history visibility is set to `Joined`
7ff8adafe9e4atagen2026-10-22 12:30 UTC1015
                let filter = if history_visibility == HistoryVisibility::Joined {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1016
                    RoomMemberships::JOIN
7ff8adafe9e4atagen2026-10-22 12:30 UTC1017
                } else {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1018
                    RoomMemberships::ACTIVE
7ff8adafe9e4atagen2026-10-22 12:30 UTC1019
                };
7ff8adafe9e4atagen2026-10-22 12:30 UTC1020
7ff8adafe9e4atagen2026-10-22 12:30 UTC1021
                let members = self.state_store.get_user_ids(room_id, filter).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1022
7ff8adafe9e4atagen2026-10-22 12:30 UTC1023
                let Some(settings) = EncryptionSettings::from_possibly_redacted(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1024
                    room_encryption_event,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1025
                    history_visibility,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1026
                    self.room_key_recipient_strategy.clone(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1027
                ) else {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1028
                    return Err(Error::EncryptionNotEnabled);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1029
                };
7ff8adafe9e4atagen2026-10-22 12:30 UTC1030
7ff8adafe9e4atagen2026-10-22 12:30 UTC1031
                Ok(o.share_room_key(room_id, members.iter().map(Deref::deref), settings).await?)
7ff8adafe9e4atagen2026-10-22 12:30 UTC1032
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1033
            None => panic!("Olm machine wasn't started"),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1034
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1035
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1036
7ff8adafe9e4atagen2026-10-22 12:30 UTC1037
    /// Get the room with the given room id.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1038
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC1039
    /// # Arguments
7ff8adafe9e4atagen2026-10-22 12:30 UTC1040
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC1041
    /// * `room_id` - The id of the room that should be fetched.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1042
    pub fn get_room(&self, room_id: &RoomId) -> Option<Room> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1043
        self.state_store.room(room_id)
7ff8adafe9e4atagen2026-10-22 12:30 UTC1044
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1045
7ff8adafe9e4atagen2026-10-22 12:30 UTC1046
    /// Forget the room with the given room ID.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1047
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC1048
    /// The room will be dropped from the room list and the store.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1049
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC1050
    /// # Arguments
7ff8adafe9e4atagen2026-10-22 12:30 UTC1051
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC1052
    /// * `room_id` - The id of the room that should be forgotten.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1053
    pub async fn forget_room(&self, room_id: &RoomId) -> Result<()> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1054
        // Forget the room in the state store.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1055
        self.state_store.forget_room(room_id).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1056
7ff8adafe9e4atagen2026-10-22 12:30 UTC1057
        // Remove the room in the event cache store too.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1058
        match self.event_cache_store().lock().await? {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1059
            // If the lock is clear, we can do the operation as expected.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1060
            // If the lock is dirty, we can ignore to refresh the state, we just need to remove a
7ff8adafe9e4atagen2026-10-22 12:30 UTC1061
            // room. Also, we must not mark the lock as non-dirty because other operations may be
7ff8adafe9e4atagen2026-10-22 12:30 UTC1062
            // critical and may need to refresh the `EventCache`' state.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1063
            EventCacheStoreLockState::Clean(guard) | EventCacheStoreLockState::Dirty(guard) => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1064
                guard.remove_room(room_id).await?
7ff8adafe9e4atagen2026-10-22 12:30 UTC1065
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1066
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1067
7ff8adafe9e4atagen2026-10-22 12:30 UTC1068
        Ok(())
7ff8adafe9e4atagen2026-10-22 12:30 UTC1069
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1070
7ff8adafe9e4atagen2026-10-22 12:30 UTC1071
    /// Get the olm machine.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1072
    #[cfg(feature = "e2e-encryption")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC1073
    pub async fn olm_machine(&self) -> RwLockReadGuard<'_, Option<OlmMachine>> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1074
        self.olm_machine.read().await
7ff8adafe9e4atagen2026-10-22 12:30 UTC1075
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1076
7ff8adafe9e4atagen2026-10-22 12:30 UTC1077
    /// Get the push rules.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1078
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC1079
    /// Gets the push rules previously processed, otherwise get them from the
7ff8adafe9e4atagen2026-10-22 12:30 UTC1080
    /// store. As a fallback, uses [`Ruleset::server_default`] if the user
7ff8adafe9e4atagen2026-10-22 12:30 UTC1081
    /// is logged in.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1082
    pub(crate) async fn get_push_rules(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1083
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1084
        global_account_data_processor: &processors::account_data::Global,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1085
    ) -> Result<Ruleset> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1086
        let _timer = timer!(Level::TRACE, "get_push_rules");
7ff8adafe9e4atagen2026-10-22 12:30 UTC1087
        if let Some(event) = global_account_data_processor
7ff8adafe9e4atagen2026-10-22 12:30 UTC1088
            .push_rules()
7ff8adafe9e4atagen2026-10-22 12:30 UTC1089
            .and_then(|ev| ev.deserialize_as_unchecked::<PushRulesEvent>().ok())
7ff8adafe9e4atagen2026-10-22 12:30 UTC1090
        {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1091
            Ok(event.content.global)
7ff8adafe9e4atagen2026-10-22 12:30 UTC1092
        } else if let Some(event) = self
7ff8adafe9e4atagen2026-10-22 12:30 UTC1093
            .state_store
7ff8adafe9e4atagen2026-10-22 12:30 UTC1094
            .get_account_data_event_static::<PushRulesEventContent>()
7ff8adafe9e4atagen2026-10-22 12:30 UTC1095
            .await?
7ff8adafe9e4atagen2026-10-22 12:30 UTC1096
            .and_then(|ev| ev.deserialize().ok())
7ff8adafe9e4atagen2026-10-22 12:30 UTC1097
        {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1098
            Ok(event.content.global)
7ff8adafe9e4atagen2026-10-22 12:30 UTC1099
        } else if let Some(session_meta) = self.state_store.session_meta() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1100
            Ok(Ruleset::server_default(&session_meta.user_id))
7ff8adafe9e4atagen2026-10-22 12:30 UTC1101
        } else {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1102
            Ok(Ruleset::new())
7ff8adafe9e4atagen2026-10-22 12:30 UTC1103
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1104
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1105
7ff8adafe9e4atagen2026-10-22 12:30 UTC1106
    /// Returns a subscriber that publishes an event every time the ignore user
7ff8adafe9e4atagen2026-10-22 12:30 UTC1107
    /// list changes
7ff8adafe9e4atagen2026-10-22 12:30 UTC1108
    pub fn subscribe_to_ignore_user_list_changes(&self) -> Subscriber<Vec<String>> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1109
        self.ignore_user_list_changes.subscribe()
7ff8adafe9e4atagen2026-10-22 12:30 UTC1110
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1111
7ff8adafe9e4atagen2026-10-22 12:30 UTC1112
    /// Returns a new receiver that gets future room info notable updates.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1113
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC1114
    /// Learn more by reading the [`RoomInfoNotableUpdate`] type.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1115
    pub fn room_info_notable_update_receiver(&self) -> broadcast::Receiver<RoomInfoNotableUpdate> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1116
        self.state_store.room_info_notable_update_sender.subscribe()
7ff8adafe9e4atagen2026-10-22 12:30 UTC1117
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1118
7ff8adafe9e4atagen2026-10-22 12:30 UTC1119
    /// Checks whether the provided `user_id` belongs to an ignored user.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1120
    pub async fn is_user_ignored(&self, user_id: &UserId) -> bool {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1121
        match self.state_store.get_account_data_event_static::<IgnoredUserListEventContent>().await
7ff8adafe9e4atagen2026-10-22 12:30 UTC1122
        {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1123
            Ok(Some(raw_ignored_user_list)) => match raw_ignored_user_list.deserialize() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1124
                Ok(current_ignored_user_list) => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1125
                    current_ignored_user_list.content.ignored_users.contains_key(user_id)
7ff8adafe9e4atagen2026-10-22 12:30 UTC1126
                }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1127
                Err(error) => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1128
                    warn!(?error, "Failed to deserialize the ignored user list event");
7ff8adafe9e4atagen2026-10-22 12:30 UTC1129
                    false
7ff8adafe9e4atagen2026-10-22 12:30 UTC1130
                }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1131
            },
7ff8adafe9e4atagen2026-10-22 12:30 UTC1132
            Ok(None) => false,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1133
            Err(error) => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1134
                warn!(?error, "Could not get the ignored user list from the state store");
7ff8adafe9e4atagen2026-10-22 12:30 UTC1135
                false
7ff8adafe9e4atagen2026-10-22 12:30 UTC1136
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1137
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1138
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1139
7ff8adafe9e4atagen2026-10-22 12:30 UTC1140
    /// Check the record of whether we are waiting for an [MSC4268] key bundle
7ff8adafe9e4atagen2026-10-22 12:30 UTC1141
    /// for the given room.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1142
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC1143
    /// [MSC4268]: https://github.com/matrix-org/matrix-spec-proposals/pull/4268
7ff8adafe9e4atagen2026-10-22 12:30 UTC1144
    #[cfg(feature = "e2e-encryption")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC1145
    pub async fn get_pending_key_bundle_details_for_room(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1146
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1147
        room_id: &RoomId,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1148
    ) -> Result<Option<RoomPendingKeyBundleDetails>> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1149
        let result = match self.olm_machine().await.as_ref() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1150
            Some(machine) => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1151
                machine.store().get_pending_key_bundle_details_for_room(room_id).await?
7ff8adafe9e4atagen2026-10-22 12:30 UTC1152
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1153
            None => None,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1154
        };
7ff8adafe9e4atagen2026-10-22 12:30 UTC1155
        Ok(result)
7ff8adafe9e4atagen2026-10-22 12:30 UTC1156
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1157
7ff8adafe9e4atagen2026-10-22 12:30 UTC1158
    /// Close all stores, releasing database connections and file locks.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1159
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC1160
    /// In-flight operations will complete before this returns.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1161
    pub async fn close_stores(&self) -> Result<()> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1162
        self.state_store.close().await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1163
        self.event_cache_store.close().await.map_err(Error::EventCacheStore)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1164
        self.media_store.close().await.map_err(Error::MediaStore)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1165
7ff8adafe9e4atagen2026-10-22 12:30 UTC1166
        #[cfg(feature = "e2e-encryption")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC1167
        self.crypto_store.close().await.map_err(Error::CryptoStore)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1168
7ff8adafe9e4atagen2026-10-22 12:30 UTC1169
        Ok(())
7ff8adafe9e4atagen2026-10-22 12:30 UTC1170
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1171
7ff8adafe9e4atagen2026-10-22 12:30 UTC1172
    /// Reopen all stores after a close, re-opening database connections.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1173
    pub async fn reopen_stores(&self) -> Result<()> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1174
        #[cfg(feature = "e2e-encryption")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC1175
        self.crypto_store.reopen().await.map_err(Error::CryptoStore)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1176
7ff8adafe9e4atagen2026-10-22 12:30 UTC1177
        self.media_store.reopen().await.map_err(Error::MediaStore)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1178
        self.event_cache_store.reopen().await.map_err(Error::EventCacheStore)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1179
        self.state_store.reopen().await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1180
7ff8adafe9e4atagen2026-10-22 12:30 UTC1181
        Ok(())
7ff8adafe9e4atagen2026-10-22 12:30 UTC1182
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1183
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC1184
7ff8adafe9e4atagen2026-10-22 12:30 UTC1185
/// Represent the `required_state` values sent by a sync request.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1186
///
7ff8adafe9e4atagen2026-10-22 12:30 UTC1187
/// This is useful to track what state events have been requested when handling
7ff8adafe9e4atagen2026-10-22 12:30 UTC1188
/// a response.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1189
///
7ff8adafe9e4atagen2026-10-22 12:30 UTC1190
/// For example, if a sync requests the `m.room.encryption` state event, and the
7ff8adafe9e4atagen2026-10-22 12:30 UTC1191
/// server replies with nothing, if means the room **is not** encrypted. Without
7ff8adafe9e4atagen2026-10-22 12:30 UTC1192
/// knowing which state event was required by the sync, it is impossible to
7ff8adafe9e4atagen2026-10-22 12:30 UTC1193
/// interpret the absence of state event from the server as _the room's
7ff8adafe9e4atagen2026-10-22 12:30 UTC1194
/// encryption state is **not encrypted**_ or _the room's encryption state is
7ff8adafe9e4atagen2026-10-22 12:30 UTC1195
/// **unknown**_.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1196
#[derive(Debug, Default)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC1197
pub struct RequestedRequiredStates {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1198
    default: Vec<(StateEventType, String)>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1199
    for_rooms: HashMap<OwnedRoomId, Vec<(StateEventType, String)>>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1200
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC1201
7ff8adafe9e4atagen2026-10-22 12:30 UTC1202
impl RequestedRequiredStates {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1203
    /// Create a new `RequestedRequiredStates`.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1204
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC1205
    /// `default` represents the `required_state` value for all rooms.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1206
    /// `for_rooms` is the `required_state` per room.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1207
    pub fn new(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1208
        default: Vec<(StateEventType, String)>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1209
        for_rooms: HashMap<OwnedRoomId, Vec<(StateEventType, String)>>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1210
    ) -> Self {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1211
        Self { default, for_rooms }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1212
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1213
7ff8adafe9e4atagen2026-10-22 12:30 UTC1214
    /// Get the `required_state` value for a specific room.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1215
    pub fn for_room(&self, room_id: &RoomId) -> &[(StateEventType, String)] {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1216
        self.for_rooms.get(room_id).unwrap_or(&self.default)
7ff8adafe9e4atagen2026-10-22 12:30 UTC1217
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1218
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC1219
7ff8adafe9e4atagen2026-10-22 12:30 UTC1220
impl From<&v5::Request> for RequestedRequiredStates {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1221
    fn from(request: &v5::Request) -> Self {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1222
        // The following information is missing in the MSC4186 at the time of writing
7ff8adafe9e4atagen2026-10-22 12:30 UTC1223
        // (2025-03-12) but: the `required_state`s from all lists and from all room
7ff8adafe9e4atagen2026-10-22 12:30 UTC1224
        // subscriptions are combined by doing an union.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1225
        //
7ff8adafe9e4atagen2026-10-22 12:30 UTC1226
        // Thus, we can do the same here, put the union in `default` and keep
7ff8adafe9e4atagen2026-10-22 12:30 UTC1227
        // `for_rooms` empty. The `Self::for_room` will automatically do the fallback.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1228
        let mut default = BTreeSet::new();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1229
7ff8adafe9e4atagen2026-10-22 12:30 UTC1230
        for list in request.lists.values() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1231
            default.extend(BTreeSet::from_iter(list.room_details.required_state.iter().cloned()));
7ff8adafe9e4atagen2026-10-22 12:30 UTC1232
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1233
7ff8adafe9e4atagen2026-10-22 12:30 UTC1234
        for room_subscription in request.room_subscriptions.values() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1235
            default.extend(BTreeSet::from_iter(room_subscription.required_state.iter().cloned()));
7ff8adafe9e4atagen2026-10-22 12:30 UTC1236
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1237
7ff8adafe9e4atagen2026-10-22 12:30 UTC1238
        Self { default: default.into_iter().collect(), for_rooms: HashMap::new() }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1239
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1240
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC1241
7ff8adafe9e4atagen2026-10-22 12:30 UTC1242
/// An enum that defines what the [`BaseClient`] should consider a DM room.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1243
#[derive(Debug, Clone, Default)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC1244
#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
7ff8adafe9e4atagen2026-10-22 12:30 UTC1245
pub enum DmRoomDefinition {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1246
    /// Standard Matrix spec definition: a room linked to a user in an
7ff8adafe9e4atagen2026-10-22 12:30 UTC1247
    /// `m.direct` event.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1248
    #[default]
7ff8adafe9e4atagen2026-10-22 12:30 UTC1249
    MatrixSpec,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1250
    /// A room that is direct, as per the spec but also contains at most 2
7ff8adafe9e4atagen2026-10-22 12:30 UTC1251
    /// active members.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1252
    TwoMembers,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1253
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC1254
7ff8adafe9e4atagen2026-10-22 12:30 UTC1255
#[cfg(test)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC1256
mod tests {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1257
    use std::collections::HashMap;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1258
7ff8adafe9e4atagen2026-10-22 12:30 UTC1259
    use assert_matches2::assert_let;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1260
    #[cfg(feature = "e2e-encryption")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC1261
    use assert_matches2::assert_matches;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1262
    use futures_util::FutureExt as _;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1263
    use matrix_sdk_common::cross_process_lock::CrossProcessLockConfig;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1264
    use matrix_sdk_test::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC1265
        BOB, InvitedRoomBuilder, LeftRoomBuilder, SyncResponseBuilder, async_test,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1266
        event_factory::EventFactory, ruma_response_from_json,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1267
    };
7ff8adafe9e4atagen2026-10-22 12:30 UTC1268
    use ruma::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC1269
        api::client::{self as api, sync::sync_events::v5},
7ff8adafe9e4atagen2026-10-22 12:30 UTC1270
        event_id,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1271
        events::{StateEventType, room::member::MembershipState},
7ff8adafe9e4atagen2026-10-22 12:30 UTC1272
        room_id,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1273
        serde::Raw,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1274
        user_id,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1275
    };
7ff8adafe9e4atagen2026-10-22 12:30 UTC1276
    use serde_json::{json, value::to_raw_value};
7ff8adafe9e4atagen2026-10-22 12:30 UTC1277
7ff8adafe9e4atagen2026-10-22 12:30 UTC1278
    use super::{BaseClient, RequestedRequiredStates};
7ff8adafe9e4atagen2026-10-22 12:30 UTC1279
    use crate::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC1280
        DmRoomDefinition, RoomDisplayName, RoomState, SessionMeta,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1281
        client::ThreadingSupport,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1282
        store::{RoomLoadSettings, StateStoreExt, StoreConfig},
7ff8adafe9e4atagen2026-10-22 12:30 UTC1283
        test_utils::logged_in_base_client,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1284
    };
7ff8adafe9e4atagen2026-10-22 12:30 UTC1285
7ff8adafe9e4atagen2026-10-22 12:30 UTC1286
    #[test]
7ff8adafe9e4atagen2026-10-22 12:30 UTC1287
    fn test_requested_required_states() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1288
        let room_id_0 = room_id!("!r0");
7ff8adafe9e4atagen2026-10-22 12:30 UTC1289
        let room_id_1 = room_id!("!r1");
7ff8adafe9e4atagen2026-10-22 12:30 UTC1290
7ff8adafe9e4atagen2026-10-22 12:30 UTC1291
        let requested_required_states = RequestedRequiredStates::new(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1292
            vec![(StateEventType::RoomAvatar, "".to_owned())],
7ff8adafe9e4atagen2026-10-22 12:30 UTC1293
            HashMap::from([(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1294
                room_id_0.to_owned(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1295
                vec![
7ff8adafe9e4atagen2026-10-22 12:30 UTC1296
                    (StateEventType::RoomMember, "foo".to_owned()),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1297
                    (StateEventType::RoomEncryption, "".to_owned()),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1298
                ],
7ff8adafe9e4atagen2026-10-22 12:30 UTC1299
            )]),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1300
        );
7ff8adafe9e4atagen2026-10-22 12:30 UTC1301
7ff8adafe9e4atagen2026-10-22 12:30 UTC1302
        // A special set of state events exists for `room_id_0`.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1303
        assert_eq!(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1304
            requested_required_states.for_room(room_id_0),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1305
            &[
7ff8adafe9e4atagen2026-10-22 12:30 UTC1306
                (StateEventType::RoomMember, "foo".to_owned()),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1307
                (StateEventType::RoomEncryption, "".to_owned()),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1308
            ]
7ff8adafe9e4atagen2026-10-22 12:30 UTC1309
        );
7ff8adafe9e4atagen2026-10-22 12:30 UTC1310
7ff8adafe9e4atagen2026-10-22 12:30 UTC1311
        // No special list for `room_id_1`, it should return the defaults.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1312
        assert_eq!(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1313
            requested_required_states.for_room(room_id_1),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1314
            &[(StateEventType::RoomAvatar, "".to_owned()),]
7ff8adafe9e4atagen2026-10-22 12:30 UTC1315
        );
7ff8adafe9e4atagen2026-10-22 12:30 UTC1316
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1317
7ff8adafe9e4atagen2026-10-22 12:30 UTC1318
    #[test]
7ff8adafe9e4atagen2026-10-22 12:30 UTC1319
    fn test_requested_required_states_from_sync_v5_request() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1320
        let room_id_0 = room_id!("!r0");
7ff8adafe9e4atagen2026-10-22 12:30 UTC1321
        let room_id_1 = room_id!("!r1");
7ff8adafe9e4atagen2026-10-22 12:30 UTC1322
7ff8adafe9e4atagen2026-10-22 12:30 UTC1323
        // Empty request.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1324
        let mut request = v5::Request::new();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1325
7ff8adafe9e4atagen2026-10-22 12:30 UTC1326
        {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1327
            let requested_required_states = RequestedRequiredStates::from(&request);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1328
7ff8adafe9e4atagen2026-10-22 12:30 UTC1329
            assert!(requested_required_states.default.is_empty());
7ff8adafe9e4atagen2026-10-22 12:30 UTC1330
            assert!(requested_required_states.for_rooms.is_empty());
7ff8adafe9e4atagen2026-10-22 12:30 UTC1331
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1332
7ff8adafe9e4atagen2026-10-22 12:30 UTC1333
        // One list.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1334
        request.lists.insert("foo".to_owned(), {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1335
            let mut list = v5::request::List::default();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1336
            list.room_details.required_state = vec![
7ff8adafe9e4atagen2026-10-22 12:30 UTC1337
                (StateEventType::RoomAvatar, "".to_owned()),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1338
                (StateEventType::RoomEncryption, "".to_owned()),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1339
            ];
7ff8adafe9e4atagen2026-10-22 12:30 UTC1340
7ff8adafe9e4atagen2026-10-22 12:30 UTC1341
            list
7ff8adafe9e4atagen2026-10-22 12:30 UTC1342
        });
7ff8adafe9e4atagen2026-10-22 12:30 UTC1343
7ff8adafe9e4atagen2026-10-22 12:30 UTC1344
        {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1345
            let requested_required_states = RequestedRequiredStates::from(&request);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1346
7ff8adafe9e4atagen2026-10-22 12:30 UTC1347
            assert_eq!(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1348
                requested_required_states.default,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1349
                &[
7ff8adafe9e4atagen2026-10-22 12:30 UTC1350
                    (StateEventType::RoomAvatar, "".to_owned()),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1351
                    (StateEventType::RoomEncryption, "".to_owned())
7ff8adafe9e4atagen2026-10-22 12:30 UTC1352
                ]
7ff8adafe9e4atagen2026-10-22 12:30 UTC1353
            );
7ff8adafe9e4atagen2026-10-22 12:30 UTC1354
            assert!(requested_required_states.for_rooms.is_empty());
7ff8adafe9e4atagen2026-10-22 12:30 UTC1355
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1356
7ff8adafe9e4atagen2026-10-22 12:30 UTC1357
        // Two lists.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1358
        request.lists.insert("bar".to_owned(), {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1359
            let mut list = v5::request::List::default();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1360
            list.room_details.required_state = vec![
7ff8adafe9e4atagen2026-10-22 12:30 UTC1361
                (StateEventType::RoomEncryption, "".to_owned()),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1362
                (StateEventType::RoomName, "".to_owned()),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1363
            ];
7ff8adafe9e4atagen2026-10-22 12:30 UTC1364
7ff8adafe9e4atagen2026-10-22 12:30 UTC1365
            list
7ff8adafe9e4atagen2026-10-22 12:30 UTC1366
        });
7ff8adafe9e4atagen2026-10-22 12:30 UTC1367
7ff8adafe9e4atagen2026-10-22 12:30 UTC1368
        {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1369
            let requested_required_states = RequestedRequiredStates::from(&request);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1370
7ff8adafe9e4atagen2026-10-22 12:30 UTC1371
            // Union of the state events.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1372
            assert_eq!(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1373
                requested_required_states.default,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1374
                &[
7ff8adafe9e4atagen2026-10-22 12:30 UTC1375
                    (StateEventType::RoomAvatar, "".to_owned()),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1376
                    (StateEventType::RoomEncryption, "".to_owned()),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1377
                    (StateEventType::RoomName, "".to_owned()),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1378
                ]
7ff8adafe9e4atagen2026-10-22 12:30 UTC1379
            );
7ff8adafe9e4atagen2026-10-22 12:30 UTC1380
            assert!(requested_required_states.for_rooms.is_empty());
7ff8adafe9e4atagen2026-10-22 12:30 UTC1381
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1382
7ff8adafe9e4atagen2026-10-22 12:30 UTC1383
        // One room subscription.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1384
        request.room_subscriptions.insert(room_id_0.to_owned(), {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1385
            let mut room_subscription = v5::request::RoomSubscription::default();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1386
7ff8adafe9e4atagen2026-10-22 12:30 UTC1387
            room_subscription.required_state = vec![
7ff8adafe9e4atagen2026-10-22 12:30 UTC1388
                (StateEventType::RoomJoinRules, "".to_owned()),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1389
                (StateEventType::RoomEncryption, "".to_owned()),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1390
            ];
7ff8adafe9e4atagen2026-10-22 12:30 UTC1391
7ff8adafe9e4atagen2026-10-22 12:30 UTC1392
            room_subscription
7ff8adafe9e4atagen2026-10-22 12:30 UTC1393
        });
7ff8adafe9e4atagen2026-10-22 12:30 UTC1394
7ff8adafe9e4atagen2026-10-22 12:30 UTC1395
        {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1396
            let requested_required_states = RequestedRequiredStates::from(&request);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1397
7ff8adafe9e4atagen2026-10-22 12:30 UTC1398
            // Union of state events, all in `default`, still nothing in `for_rooms`.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1399
            assert_eq!(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1400
                requested_required_states.default,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1401
                &[
7ff8adafe9e4atagen2026-10-22 12:30 UTC1402
                    (StateEventType::RoomAvatar, "".to_owned()),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1403
                    (StateEventType::RoomEncryption, "".to_owned()),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1404
                    (StateEventType::RoomJoinRules, "".to_owned()),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1405
                    (StateEventType::RoomName, "".to_owned()),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1406
                ]
7ff8adafe9e4atagen2026-10-22 12:30 UTC1407
            );
7ff8adafe9e4atagen2026-10-22 12:30 UTC1408
            assert!(requested_required_states.for_rooms.is_empty());
7ff8adafe9e4atagen2026-10-22 12:30 UTC1409
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1410
7ff8adafe9e4atagen2026-10-22 12:30 UTC1411
        // Two room subscriptions.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1412
        request.room_subscriptions.insert(room_id_1.to_owned(), {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1413
            let mut room_subscription = v5::request::RoomSubscription::default();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1414
7ff8adafe9e4atagen2026-10-22 12:30 UTC1415
            room_subscription.required_state = vec![
7ff8adafe9e4atagen2026-10-22 12:30 UTC1416
                (StateEventType::RoomName, "".to_owned()),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1417
                (StateEventType::RoomTopic, "".to_owned()),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1418
            ];
7ff8adafe9e4atagen2026-10-22 12:30 UTC1419
7ff8adafe9e4atagen2026-10-22 12:30 UTC1420
            room_subscription
7ff8adafe9e4atagen2026-10-22 12:30 UTC1421
        });
7ff8adafe9e4atagen2026-10-22 12:30 UTC1422
7ff8adafe9e4atagen2026-10-22 12:30 UTC1423
        {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1424
            let requested_required_states = RequestedRequiredStates::from(&request);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1425
7ff8adafe9e4atagen2026-10-22 12:30 UTC1426
            // Union of state events, all in `default`, still nothing in `for_rooms`.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1427
            assert_eq!(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1428
                requested_required_states.default,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1429
                &[
7ff8adafe9e4atagen2026-10-22 12:30 UTC1430
                    (StateEventType::RoomAvatar, "".to_owned()),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1431
                    (StateEventType::RoomEncryption, "".to_owned()),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1432
                    (StateEventType::RoomJoinRules, "".to_owned()),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1433
                    (StateEventType::RoomName, "".to_owned()),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1434
                    (StateEventType::RoomTopic, "".to_owned()),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1435
                ]
7ff8adafe9e4atagen2026-10-22 12:30 UTC1436
            );
7ff8adafe9e4atagen2026-10-22 12:30 UTC1437
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1438
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1439
7ff8adafe9e4atagen2026-10-22 12:30 UTC1440
    #[async_test]
7ff8adafe9e4atagen2026-10-22 12:30 UTC1441
    async fn test_invite_after_leaving() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1442
        let user_id = user_id!("@alice:example.org");
7ff8adafe9e4atagen2026-10-22 12:30 UTC1443
        let room_id = room_id!("!test:example.org");
7ff8adafe9e4atagen2026-10-22 12:30 UTC1444
7ff8adafe9e4atagen2026-10-22 12:30 UTC1445
        let client = logged_in_base_client(Some(user_id)).await;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1446
        let f = EventFactory::new();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1447
7ff8adafe9e4atagen2026-10-22 12:30 UTC1448
        let mut sync_builder = SyncResponseBuilder::new();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1449
7ff8adafe9e4atagen2026-10-22 12:30 UTC1450
        let response = sync_builder
7ff8adafe9e4atagen2026-10-22 12:30 UTC1451
            .add_left_room(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1452
                LeftRoomBuilder::new(room_id).add_timeline_event(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1453
                    EventFactory::new()
7ff8adafe9e4atagen2026-10-22 12:30 UTC1454
                        .member(user_id)
7ff8adafe9e4atagen2026-10-22 12:30 UTC1455
                        .membership(MembershipState::Leave)
7ff8adafe9e4atagen2026-10-22 12:30 UTC1456
                        .display_name("Alice")
7ff8adafe9e4atagen2026-10-22 12:30 UTC1457
                        .event_id(event_id!("$994173582443PhrSn:example.org")),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1458
                ),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1459
            )
7ff8adafe9e4atagen2026-10-22 12:30 UTC1460
            .build_sync_response();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1461
        client.receive_sync_response(response).await.unwrap();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1462
        assert_eq!(client.get_room(room_id).unwrap().state(), RoomState::Left);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1463
7ff8adafe9e4atagen2026-10-22 12:30 UTC1464
        let response = sync_builder
7ff8adafe9e4atagen2026-10-22 12:30 UTC1465
            .add_invited_room(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1466
                InvitedRoomBuilder::new(room_id).add_state_event(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1467
                    f.member(user_id)
7ff8adafe9e4atagen2026-10-22 12:30 UTC1468
                        .sender(user_id!("@example:example.org"))
7ff8adafe9e4atagen2026-10-22 12:30 UTC1469
                        .membership(MembershipState::Invite)
7ff8adafe9e4atagen2026-10-22 12:30 UTC1470
                        .display_name("Alice"),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1471
                ),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1472
            )
7ff8adafe9e4atagen2026-10-22 12:30 UTC1473
            .build_sync_response();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1474
        client.receive_sync_response(response).await.unwrap();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1475
        assert_eq!(client.get_room(room_id).unwrap().state(), RoomState::Invited);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1476
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1477
7ff8adafe9e4atagen2026-10-22 12:30 UTC1478
    #[async_test]
7ff8adafe9e4atagen2026-10-22 12:30 UTC1479
    async fn test_invite_displayname() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1480
        let user_id = user_id!("@alice:example.org");
7ff8adafe9e4atagen2026-10-22 12:30 UTC1481
        let room_id = room_id!("!ithpyNKDtmhneaTQja:example.org");
7ff8adafe9e4atagen2026-10-22 12:30 UTC1482
7ff8adafe9e4atagen2026-10-22 12:30 UTC1483
        let client = logged_in_base_client(Some(user_id)).await;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1484
7ff8adafe9e4atagen2026-10-22 12:30 UTC1485
        let response = ruma_response_from_json(&json!({
7ff8adafe9e4atagen2026-10-22 12:30 UTC1486
            "next_batch": "asdkl;fjasdkl;fj;asdkl;f",
7ff8adafe9e4atagen2026-10-22 12:30 UTC1487
            "device_one_time_keys_count": {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1488
                "signed_curve25519": 50u64
7ff8adafe9e4atagen2026-10-22 12:30 UTC1489
            },
7ff8adafe9e4atagen2026-10-22 12:30 UTC1490
            "device_unused_fallback_key_types": [
7ff8adafe9e4atagen2026-10-22 12:30 UTC1491
                "signed_curve25519"
7ff8adafe9e4atagen2026-10-22 12:30 UTC1492
            ],
7ff8adafe9e4atagen2026-10-22 12:30 UTC1493
            "rooms": {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1494
                "invite": {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1495
                    "!ithpyNKDtmhneaTQja:example.org": {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1496
                        "invite_state": {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1497
                            "events": [
7ff8adafe9e4atagen2026-10-22 12:30 UTC1498
                                {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1499
                                    "content": {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1500
                                        "creator": "@test:example.org",
7ff8adafe9e4atagen2026-10-22 12:30 UTC1501
                                        "room_version": "9"
7ff8adafe9e4atagen2026-10-22 12:30 UTC1502
                                    },
7ff8adafe9e4atagen2026-10-22 12:30 UTC1503
                                    "sender": "@test:example.org",
7ff8adafe9e4atagen2026-10-22 12:30 UTC1504
                                    "state_key": "",
7ff8adafe9e4atagen2026-10-22 12:30 UTC1505
                                    "type": "m.room.create"
7ff8adafe9e4atagen2026-10-22 12:30 UTC1506
                                },
7ff8adafe9e4atagen2026-10-22 12:30 UTC1507
                                {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1508
                                    "content": {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1509
                                        "join_rule": "invite"
7ff8adafe9e4atagen2026-10-22 12:30 UTC1510
                                    },
7ff8adafe9e4atagen2026-10-22 12:30 UTC1511
                                    "sender": "@test:example.org",
7ff8adafe9e4atagen2026-10-22 12:30 UTC1512
                                    "state_key": "",
7ff8adafe9e4atagen2026-10-22 12:30 UTC1513
                                    "type": "m.room.join_rules"
7ff8adafe9e4atagen2026-10-22 12:30 UTC1514
                                },
7ff8adafe9e4atagen2026-10-22 12:30 UTC1515
                                {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1516
                                    "content": {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1517
                                        "algorithm": "m.megolm.v1.aes-sha2"
7ff8adafe9e4atagen2026-10-22 12:30 UTC1518
                                    },
7ff8adafe9e4atagen2026-10-22 12:30 UTC1519
                                    "sender": "@test:example.org",
7ff8adafe9e4atagen2026-10-22 12:30 UTC1520
                                    "state_key": "",
7ff8adafe9e4atagen2026-10-22 12:30 UTC1521
                                    "type": "m.room.encryption"
7ff8adafe9e4atagen2026-10-22 12:30 UTC1522
                                },
7ff8adafe9e4atagen2026-10-22 12:30 UTC1523
                                {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1524
                                    "content": {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1525
                                        "avatar_url": "mxc://example.org/dcBBDwuWEUrjfrOchvkirUST",
7ff8adafe9e4atagen2026-10-22 12:30 UTC1526
                                        "displayname": "Kyra",
7ff8adafe9e4atagen2026-10-22 12:30 UTC1527
                                        "membership": "join"
7ff8adafe9e4atagen2026-10-22 12:30 UTC1528
                                    },
7ff8adafe9e4atagen2026-10-22 12:30 UTC1529
                                    "sender": "@test:example.org",
7ff8adafe9e4atagen2026-10-22 12:30 UTC1530
                                    "state_key": "@test:example.org",
7ff8adafe9e4atagen2026-10-22 12:30 UTC1531
                                    "type": "m.room.member"
7ff8adafe9e4atagen2026-10-22 12:30 UTC1532
                                },
7ff8adafe9e4atagen2026-10-22 12:30 UTC1533
                                {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1534
                                    "content": {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1535
                                        "avatar_url": "mxc://example.org/ABFEXSDrESxovWwEnCYdNcHT",
7ff8adafe9e4atagen2026-10-22 12:30 UTC1536
                                        "displayname": "alice",
7ff8adafe9e4atagen2026-10-22 12:30 UTC1537
                                        "is_direct": true,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1538
                                        "membership": "invite"
7ff8adafe9e4atagen2026-10-22 12:30 UTC1539
                                    },
7ff8adafe9e4atagen2026-10-22 12:30 UTC1540
                                    "origin_server_ts": 1650878657984u64,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1541
                                    "sender": "@test:example.org",
7ff8adafe9e4atagen2026-10-22 12:30 UTC1542
                                    "state_key": "@alice:example.org",
7ff8adafe9e4atagen2026-10-22 12:30 UTC1543
                                    "type": "m.room.member",
7ff8adafe9e4atagen2026-10-22 12:30 UTC1544
                                    "unsigned": {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1545
                                        "age": 14u64
7ff8adafe9e4atagen2026-10-22 12:30 UTC1546
                                    },
7ff8adafe9e4atagen2026-10-22 12:30 UTC1547
                                    "event_id": "$fLDqltg9Puj-kWItLSFVHPGN4YkgpYQf2qImPzdmgrE"
7ff8adafe9e4atagen2026-10-22 12:30 UTC1548
                                }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1549
                            ]
7ff8adafe9e4atagen2026-10-22 12:30 UTC1550
                        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1551
                    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1552
                }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1553
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1554
        }));
7ff8adafe9e4atagen2026-10-22 12:30 UTC1555
7ff8adafe9e4atagen2026-10-22 12:30 UTC1556
        client.receive_sync_response(response).await.unwrap();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1557
7ff8adafe9e4atagen2026-10-22 12:30 UTC1558
        let room = client.get_room(room_id).expect("Room not found");
7ff8adafe9e4atagen2026-10-22 12:30 UTC1559
        assert_eq!(room.state(), RoomState::Invited);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1560
        assert_eq!(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1561
            room.compute_display_name().await.expect("fetching display name failed").into_inner(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1562
            RoomDisplayName::Calculated("Kyra".to_owned())
7ff8adafe9e4atagen2026-10-22 12:30 UTC1563
        );
7ff8adafe9e4atagen2026-10-22 12:30 UTC1564
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1565
7ff8adafe9e4atagen2026-10-22 12:30 UTC1566
    #[async_test]
7ff8adafe9e4atagen2026-10-22 12:30 UTC1567
    async fn test_deserialization_failure() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1568
        let user_id = user_id!("@alice:example.org");
7ff8adafe9e4atagen2026-10-22 12:30 UTC1569
        let room_id = room_id!("!ithpyNKDtmhneaTQja:example.org");
7ff8adafe9e4atagen2026-10-22 12:30 UTC1570
7ff8adafe9e4atagen2026-10-22 12:30 UTC1571
        let client = BaseClient::new(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1572
            StoreConfig::new(CrossProcessLockConfig::SingleProcess),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1573
            ThreadingSupport::Disabled,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1574
            DmRoomDefinition::default(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1575
        );
7ff8adafe9e4atagen2026-10-22 12:30 UTC1576
        client
7ff8adafe9e4atagen2026-10-22 12:30 UTC1577
            .activate(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1578
                SessionMeta { user_id: user_id.to_owned(), device_id: "FOOBAR".into() },
7ff8adafe9e4atagen2026-10-22 12:30 UTC1579
                RoomLoadSettings::default(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1580
                #[cfg(feature = "e2e-encryption")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC1581
                None,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1582
            )
7ff8adafe9e4atagen2026-10-22 12:30 UTC1583
            .await
7ff8adafe9e4atagen2026-10-22 12:30 UTC1584
            .unwrap();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1585
7ff8adafe9e4atagen2026-10-22 12:30 UTC1586
        let response = ruma_response_from_json(&json!({
7ff8adafe9e4atagen2026-10-22 12:30 UTC1587
            "next_batch": "asdkl;fjasdkl;fj;asdkl;f",
7ff8adafe9e4atagen2026-10-22 12:30 UTC1588
            "rooms": {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1589
                "join": {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1590
                    "!ithpyNKDtmhneaTQja:example.org": {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1591
                        "state": {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1592
                            "events": [
7ff8adafe9e4atagen2026-10-22 12:30 UTC1593
                                {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1594
                                    "invalid": "invalid",
7ff8adafe9e4atagen2026-10-22 12:30 UTC1595
                                },
7ff8adafe9e4atagen2026-10-22 12:30 UTC1596
                                {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1597
                                    "content": {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1598
                                        "name": "The room name"
7ff8adafe9e4atagen2026-10-22 12:30 UTC1599
                                    },
7ff8adafe9e4atagen2026-10-22 12:30 UTC1600
                                    "event_id": "$143273582443PhrSn:example.org",
7ff8adafe9e4atagen2026-10-22 12:30 UTC1601
                                    "origin_server_ts": 1432735824653u64,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1602
                                    "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
7ff8adafe9e4atagen2026-10-22 12:30 UTC1603
                                    "sender": "@example:example.org",
7ff8adafe9e4atagen2026-10-22 12:30 UTC1604
                                    "state_key": "",
7ff8adafe9e4atagen2026-10-22 12:30 UTC1605
                                    "type": "m.room.name",
7ff8adafe9e4atagen2026-10-22 12:30 UTC1606
                                    "unsigned": {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1607
                                        "age": 1234
7ff8adafe9e4atagen2026-10-22 12:30 UTC1608
                                    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1609
                                },
7ff8adafe9e4atagen2026-10-22 12:30 UTC1610
                            ]
7ff8adafe9e4atagen2026-10-22 12:30 UTC1611
                        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1612
                    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1613
                }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1614
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1615
        }));
7ff8adafe9e4atagen2026-10-22 12:30 UTC1616
7ff8adafe9e4atagen2026-10-22 12:30 UTC1617
        client.receive_sync_response(response).await.unwrap();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1618
        client
7ff8adafe9e4atagen2026-10-22 12:30 UTC1619
            .state_store()
7ff8adafe9e4atagen2026-10-22 12:30 UTC1620
            .get_state_event_static::<ruma::events::room::name::RoomNameEventContent>(room_id)
7ff8adafe9e4atagen2026-10-22 12:30 UTC1621
            .await
7ff8adafe9e4atagen2026-10-22 12:30 UTC1622
            .expect("Failed to fetch state event")
7ff8adafe9e4atagen2026-10-22 12:30 UTC1623
            .expect("State event not found")
7ff8adafe9e4atagen2026-10-22 12:30 UTC1624
            .deserialize()
7ff8adafe9e4atagen2026-10-22 12:30 UTC1625
            .expect("Failed to deserialize state event");
7ff8adafe9e4atagen2026-10-22 12:30 UTC1626
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1627
7ff8adafe9e4atagen2026-10-22 12:30 UTC1628
    #[async_test]
7ff8adafe9e4atagen2026-10-22 12:30 UTC1629
    async fn test_invited_members_arent_ignored() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1630
        let user_id = user_id!("@alice:example.org");
7ff8adafe9e4atagen2026-10-22 12:30 UTC1631
        let inviter_user_id = user_id!("@bob:example.org");
7ff8adafe9e4atagen2026-10-22 12:30 UTC1632
        let room_id = room_id!("!ithpyNKDtmhneaTQja:example.org");
7ff8adafe9e4atagen2026-10-22 12:30 UTC1633
7ff8adafe9e4atagen2026-10-22 12:30 UTC1634
        let client = BaseClient::new(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1635
            StoreConfig::new(CrossProcessLockConfig::SingleProcess),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1636
            ThreadingSupport::Disabled,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1637
            DmRoomDefinition::default(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1638
        );
7ff8adafe9e4atagen2026-10-22 12:30 UTC1639
        client
7ff8adafe9e4atagen2026-10-22 12:30 UTC1640
            .activate(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1641
                SessionMeta { user_id: user_id.to_owned(), device_id: "FOOBAR".into() },
7ff8adafe9e4atagen2026-10-22 12:30 UTC1642
                RoomLoadSettings::default(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1643
                #[cfg(feature = "e2e-encryption")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC1644
                None,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1645
            )
7ff8adafe9e4atagen2026-10-22 12:30 UTC1646
            .await
7ff8adafe9e4atagen2026-10-22 12:30 UTC1647
            .unwrap();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1648
7ff8adafe9e4atagen2026-10-22 12:30 UTC1649
        // Preamble: let the SDK know about the room.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1650
        let mut sync_builder = SyncResponseBuilder::new();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1651
        let response = sync_builder
7ff8adafe9e4atagen2026-10-22 12:30 UTC1652
            .add_joined_room(matrix_sdk_test::JoinedRoomBuilder::new(room_id))
7ff8adafe9e4atagen2026-10-22 12:30 UTC1653
            .build_sync_response();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1654
        client.receive_sync_response(response).await.unwrap();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1655
7ff8adafe9e4atagen2026-10-22 12:30 UTC1656
        // When I process the result of a /members request that only contains an invited
7ff8adafe9e4atagen2026-10-22 12:30 UTC1657
        // member,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1658
        let request = api::membership::get_member_events::v3::Request::new(room_id.to_owned());
7ff8adafe9e4atagen2026-10-22 12:30 UTC1659
7ff8adafe9e4atagen2026-10-22 12:30 UTC1660
        let raw_member_event = json!({
7ff8adafe9e4atagen2026-10-22 12:30 UTC1661
            "content": {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1662
                "avatar_url": "mxc://localhost/fewjilfewjil42",
7ff8adafe9e4atagen2026-10-22 12:30 UTC1663
                "displayname": "Invited Alice",
7ff8adafe9e4atagen2026-10-22 12:30 UTC1664
                "membership": "invite"
7ff8adafe9e4atagen2026-10-22 12:30 UTC1665
            },
7ff8adafe9e4atagen2026-10-22 12:30 UTC1666
            "event_id": "$151800140517rfvjc:localhost",
7ff8adafe9e4atagen2026-10-22 12:30 UTC1667
            "origin_server_ts": 151800140,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1668
            "room_id": room_id,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1669
            "sender": inviter_user_id,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1670
            "state_key": user_id,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1671
            "type": "m.room.member",
7ff8adafe9e4atagen2026-10-22 12:30 UTC1672
            "unsigned": {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1673
                "age": 13374242,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1674
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1675
        });
7ff8adafe9e4atagen2026-10-22 12:30 UTC1676
        let response = api::membership::get_member_events::v3::Response::new(vec![Raw::from_json(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1677
            to_raw_value(&raw_member_event).unwrap(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1678
        )]);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1679
7ff8adafe9e4atagen2026-10-22 12:30 UTC1680
        // It's correctly processed,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1681
        client.receive_all_members(room_id, &request, &response).await.unwrap();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1682
7ff8adafe9e4atagen2026-10-22 12:30 UTC1683
        let room = client.get_room(room_id).unwrap();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1684
7ff8adafe9e4atagen2026-10-22 12:30 UTC1685
        // And I can get the invited member display name and avatar.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1686
        let member = room.get_member(user_id).await.expect("ok").expect("exists");
7ff8adafe9e4atagen2026-10-22 12:30 UTC1687
7ff8adafe9e4atagen2026-10-22 12:30 UTC1688
        assert_eq!(member.user_id(), user_id);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1689
        assert_eq!(member.display_name().unwrap(), "Invited Alice");
7ff8adafe9e4atagen2026-10-22 12:30 UTC1690
        assert_eq!(member.avatar_url().unwrap().to_string(), "mxc://localhost/fewjilfewjil42");
7ff8adafe9e4atagen2026-10-22 12:30 UTC1691
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1692
7ff8adafe9e4atagen2026-10-22 12:30 UTC1693
    #[async_test]
7ff8adafe9e4atagen2026-10-22 12:30 UTC1694
    async fn test_reinvited_members_get_a_display_name() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1695
        let user_id = user_id!("@alice:example.org");
7ff8adafe9e4atagen2026-10-22 12:30 UTC1696
        let inviter_user_id = user_id!("@bob:example.org");
7ff8adafe9e4atagen2026-10-22 12:30 UTC1697
        let room_id = room_id!("!ithpyNKDtmhneaTQja:example.org");
7ff8adafe9e4atagen2026-10-22 12:30 UTC1698
7ff8adafe9e4atagen2026-10-22 12:30 UTC1699
        let client = BaseClient::new(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1700
            StoreConfig::new(CrossProcessLockConfig::SingleProcess),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1701
            ThreadingSupport::Disabled,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1702
            DmRoomDefinition::default(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1703
        );
7ff8adafe9e4atagen2026-10-22 12:30 UTC1704
        client
7ff8adafe9e4atagen2026-10-22 12:30 UTC1705
            .activate(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1706
                SessionMeta { user_id: user_id.to_owned(), device_id: "FOOBAR".into() },
7ff8adafe9e4atagen2026-10-22 12:30 UTC1707
                RoomLoadSettings::default(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1708
                #[cfg(feature = "e2e-encryption")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC1709
                None,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1710
            )
7ff8adafe9e4atagen2026-10-22 12:30 UTC1711
            .await
7ff8adafe9e4atagen2026-10-22 12:30 UTC1712
            .unwrap();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1713
7ff8adafe9e4atagen2026-10-22 12:30 UTC1714
        // Preamble: let the SDK know about the room, and that the invited user left it.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1715
        let f = EventFactory::new().sender(user_id);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1716
        let mut sync_builder = SyncResponseBuilder::new();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1717
        let response = sync_builder
7ff8adafe9e4atagen2026-10-22 12:30 UTC1718
            .add_joined_room(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1719
                matrix_sdk_test::JoinedRoomBuilder::new(room_id)
7ff8adafe9e4atagen2026-10-22 12:30 UTC1720
                    .add_state_event(f.member(user_id).leave()),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1721
            )
7ff8adafe9e4atagen2026-10-22 12:30 UTC1722
            .build_sync_response();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1723
        client.receive_sync_response(response).await.unwrap();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1724
7ff8adafe9e4atagen2026-10-22 12:30 UTC1725
        // Now, say that the user has been re-invited.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1726
        let request = api::membership::get_member_events::v3::Request::new(room_id.to_owned());
7ff8adafe9e4atagen2026-10-22 12:30 UTC1727
7ff8adafe9e4atagen2026-10-22 12:30 UTC1728
        let raw_member_event = json!({
7ff8adafe9e4atagen2026-10-22 12:30 UTC1729
            "content": {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1730
                "avatar_url": "mxc://localhost/fewjilfewjil42",
7ff8adafe9e4atagen2026-10-22 12:30 UTC1731
                "displayname": "Invited Alice",
7ff8adafe9e4atagen2026-10-22 12:30 UTC1732
                "membership": "invite"
7ff8adafe9e4atagen2026-10-22 12:30 UTC1733
            },
7ff8adafe9e4atagen2026-10-22 12:30 UTC1734
            "event_id": "$151800140517rfvjc:localhost",
7ff8adafe9e4atagen2026-10-22 12:30 UTC1735
            "origin_server_ts": 151800140,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1736
            "room_id": room_id,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1737
            "sender": inviter_user_id,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1738
            "state_key": user_id,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1739
            "type": "m.room.member",
7ff8adafe9e4atagen2026-10-22 12:30 UTC1740
            "unsigned": {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1741
                "age": 13374242,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1742
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1743
        });
7ff8adafe9e4atagen2026-10-22 12:30 UTC1744
        let response = api::membership::get_member_events::v3::Response::new(vec![Raw::from_json(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1745
            to_raw_value(&raw_member_event).unwrap(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1746
        )]);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1747
7ff8adafe9e4atagen2026-10-22 12:30 UTC1748
        // It's correctly processed,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1749
        client.receive_all_members(room_id, &request, &response).await.unwrap();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1750
7ff8adafe9e4atagen2026-10-22 12:30 UTC1751
        let room = client.get_room(room_id).unwrap();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1752
7ff8adafe9e4atagen2026-10-22 12:30 UTC1753
        // And I can get the invited member display name and avatar.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1754
        let member = room.get_member(user_id).await.expect("ok").expect("exists");
7ff8adafe9e4atagen2026-10-22 12:30 UTC1755
7ff8adafe9e4atagen2026-10-22 12:30 UTC1756
        assert_eq!(member.user_id(), user_id);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1757
        assert_eq!(member.display_name().unwrap(), "Invited Alice");
7ff8adafe9e4atagen2026-10-22 12:30 UTC1758
        assert_eq!(member.avatar_url().unwrap().to_string(), "mxc://localhost/fewjilfewjil42");
7ff8adafe9e4atagen2026-10-22 12:30 UTC1759
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1760
7ff8adafe9e4atagen2026-10-22 12:30 UTC1761
    #[async_test]
7ff8adafe9e4atagen2026-10-22 12:30 UTC1762
    async fn test_ignored_user_list_changes() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1763
        let user_id = user_id!("@alice:example.org");
7ff8adafe9e4atagen2026-10-22 12:30 UTC1764
        let client = BaseClient::new(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1765
            StoreConfig::new(CrossProcessLockConfig::SingleProcess),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1766
            ThreadingSupport::Disabled,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1767
            DmRoomDefinition::default(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1768
        );
7ff8adafe9e4atagen2026-10-22 12:30 UTC1769
7ff8adafe9e4atagen2026-10-22 12:30 UTC1770
        client
7ff8adafe9e4atagen2026-10-22 12:30 UTC1771
            .activate(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1772
                SessionMeta { user_id: user_id.to_owned(), device_id: "FOOBAR".into() },
7ff8adafe9e4atagen2026-10-22 12:30 UTC1773
                RoomLoadSettings::default(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1774
                #[cfg(feature = "e2e-encryption")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC1775
                None,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1776
            )
7ff8adafe9e4atagen2026-10-22 12:30 UTC1777
            .await
7ff8adafe9e4atagen2026-10-22 12:30 UTC1778
            .unwrap();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1779
7ff8adafe9e4atagen2026-10-22 12:30 UTC1780
        let mut subscriber = client.subscribe_to_ignore_user_list_changes();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1781
        assert!(subscriber.next().now_or_never().is_none());
7ff8adafe9e4atagen2026-10-22 12:30 UTC1782
7ff8adafe9e4atagen2026-10-22 12:30 UTC1783
        let f = EventFactory::new();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1784
        let mut sync_builder = SyncResponseBuilder::new();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1785
        let response = sync_builder
7ff8adafe9e4atagen2026-10-22 12:30 UTC1786
            .add_global_account_data(f.ignored_user_list([(*BOB).into()]))
7ff8adafe9e4atagen2026-10-22 12:30 UTC1787
            .build_sync_response();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1788
        client.receive_sync_response(response).await.unwrap();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1789
7ff8adafe9e4atagen2026-10-22 12:30 UTC1790
        assert_let!(Some(ignored) = subscriber.next().await);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1791
        assert_eq!(ignored, [BOB.to_string()]);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1792
7ff8adafe9e4atagen2026-10-22 12:30 UTC1793
        // Receive the same response.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1794
        let response = sync_builder
7ff8adafe9e4atagen2026-10-22 12:30 UTC1795
            .add_global_account_data(f.ignored_user_list([(*BOB).into()]))
7ff8adafe9e4atagen2026-10-22 12:30 UTC1796
            .build_sync_response();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1797
        client.receive_sync_response(response).await.unwrap();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1798
7ff8adafe9e4atagen2026-10-22 12:30 UTC1799
        // No changes in the ignored list.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1800
        assert!(subscriber.next().now_or_never().is_none());
7ff8adafe9e4atagen2026-10-22 12:30 UTC1801
7ff8adafe9e4atagen2026-10-22 12:30 UTC1802
        // Now remove Bob from the ignored list.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1803
        let response =
7ff8adafe9e4atagen2026-10-22 12:30 UTC1804
            sync_builder.add_global_account_data(f.ignored_user_list([])).build_sync_response();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1805
        client.receive_sync_response(response).await.unwrap();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1806
7ff8adafe9e4atagen2026-10-22 12:30 UTC1807
        assert_let!(Some(ignored) = subscriber.next().await);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1808
        assert!(ignored.is_empty());
7ff8adafe9e4atagen2026-10-22 12:30 UTC1809
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1810
7ff8adafe9e4atagen2026-10-22 12:30 UTC1811
    #[async_test]
7ff8adafe9e4atagen2026-10-22 12:30 UTC1812
    async fn test_is_user_ignored() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1813
        let ignored_user_id = user_id!("@alice:example.org");
7ff8adafe9e4atagen2026-10-22 12:30 UTC1814
        let client = logged_in_base_client(None).await;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1815
7ff8adafe9e4atagen2026-10-22 12:30 UTC1816
        let mut sync_builder = SyncResponseBuilder::new();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1817
        let f = EventFactory::new();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1818
        let response = sync_builder
7ff8adafe9e4atagen2026-10-22 12:30 UTC1819
            .add_global_account_data(f.ignored_user_list([ignored_user_id.to_owned()]))
7ff8adafe9e4atagen2026-10-22 12:30 UTC1820
            .build_sync_response();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1821
        client.receive_sync_response(response).await.unwrap();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1822
7ff8adafe9e4atagen2026-10-22 12:30 UTC1823
        assert!(client.is_user_ignored(ignored_user_id).await);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1824
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1825
7ff8adafe9e4atagen2026-10-22 12:30 UTC1826
    #[cfg(feature = "e2e-encryption")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC1827
    #[async_test]
7ff8adafe9e4atagen2026-10-22 12:30 UTC1828
    async fn test_invite_details_are_set() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1829
        let user_id = user_id!("@alice:localhost");
7ff8adafe9e4atagen2026-10-22 12:30 UTC1830
        let client = logged_in_base_client(Some(user_id)).await;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1831
        let known_room_id = room_id!("!invited:localhost");
7ff8adafe9e4atagen2026-10-22 12:30 UTC1832
        let unknown_room_id = room_id!("!unknown:localhost");
7ff8adafe9e4atagen2026-10-22 12:30 UTC1833
7ff8adafe9e4atagen2026-10-22 12:30 UTC1834
        let mut sync_builder = SyncResponseBuilder::new();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1835
        let response = sync_builder
7ff8adafe9e4atagen2026-10-22 12:30 UTC1836
            .add_invited_room(InvitedRoomBuilder::new(known_room_id))
7ff8adafe9e4atagen2026-10-22 12:30 UTC1837
            .build_sync_response();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1838
        client.receive_sync_response(response).await.unwrap();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1839
7ff8adafe9e4atagen2026-10-22 12:30 UTC1840
        // Let us first check the initial state, we should have a room in the invite
7ff8adafe9e4atagen2026-10-22 12:30 UTC1841
        // state.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1842
        let invited_room = client
7ff8adafe9e4atagen2026-10-22 12:30 UTC1843
            .get_room(known_room_id)
7ff8adafe9e4atagen2026-10-22 12:30 UTC1844
            .expect("The sync should have created a room in the invited state");
7ff8adafe9e4atagen2026-10-22 12:30 UTC1845
7ff8adafe9e4atagen2026-10-22 12:30 UTC1846
        assert_eq!(invited_room.state(), RoomState::Invited);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1847
        assert!(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1848
            client.get_pending_key_bundle_details_for_room(known_room_id).await.unwrap().is_none()
7ff8adafe9e4atagen2026-10-22 12:30 UTC1849
        );
7ff8adafe9e4atagen2026-10-22 12:30 UTC1850
7ff8adafe9e4atagen2026-10-22 12:30 UTC1851
        // Now we join the room.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1852
        let joined_room = client
7ff8adafe9e4atagen2026-10-22 12:30 UTC1853
            .room_joined(known_room_id, Some(user_id.to_owned()))
7ff8adafe9e4atagen2026-10-22 12:30 UTC1854
            .await
7ff8adafe9e4atagen2026-10-22 12:30 UTC1855
            .expect("We should be able to mark a room as joined");
7ff8adafe9e4atagen2026-10-22 12:30 UTC1856
7ff8adafe9e4atagen2026-10-22 12:30 UTC1857
        // Yup, we now have some invite details.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1858
        assert_eq!(joined_room.state(), RoomState::Joined);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1859
        assert_matches!(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1860
            client.get_pending_key_bundle_details_for_room(known_room_id).await,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1861
            Ok(Some(details))
7ff8adafe9e4atagen2026-10-22 12:30 UTC1862
        );
7ff8adafe9e4atagen2026-10-22 12:30 UTC1863
        assert_eq!(details.inviter, user_id);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1864
7ff8adafe9e4atagen2026-10-22 12:30 UTC1865
        // If we didn't know about the room before the join, we assume that there wasn't
7ff8adafe9e4atagen2026-10-22 12:30 UTC1866
        // an invite and we don't record the timestamp.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1867
        assert!(client.get_room(unknown_room_id).is_none());
7ff8adafe9e4atagen2026-10-22 12:30 UTC1868
        let unknown_room = client
7ff8adafe9e4atagen2026-10-22 12:30 UTC1869
            .room_joined(unknown_room_id, Some(user_id.to_owned()))
7ff8adafe9e4atagen2026-10-22 12:30 UTC1870
            .await
7ff8adafe9e4atagen2026-10-22 12:30 UTC1871
            .expect("We should be able to mark a room as joined");
7ff8adafe9e4atagen2026-10-22 12:30 UTC1872
7ff8adafe9e4atagen2026-10-22 12:30 UTC1873
        assert_eq!(unknown_room.state(), RoomState::Joined);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1874
        assert!(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1875
            client
7ff8adafe9e4atagen2026-10-22 12:30 UTC1876
                .get_pending_key_bundle_details_for_room(unknown_room_id)
7ff8adafe9e4atagen2026-10-22 12:30 UTC1877
                .await
7ff8adafe9e4atagen2026-10-22 12:30 UTC1878
                .unwrap()
7ff8adafe9e4atagen2026-10-22 12:30 UTC1879
                .is_none()
7ff8adafe9e4atagen2026-10-22 12:30 UTC1880
        );
7ff8adafe9e4atagen2026-10-22 12:30 UTC1881
7ff8adafe9e4atagen2026-10-22 12:30 UTC1882
        sync_builder.clear();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1883
        let response =
7ff8adafe9e4atagen2026-10-22 12:30 UTC1884
            sync_builder.add_left_room(LeftRoomBuilder::new(known_room_id)).build_sync_response();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1885
        client.receive_sync_response(response).await.unwrap();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1886
7ff8adafe9e4atagen2026-10-22 12:30 UTC1887
        // Now that we left the room, we shouldn't have any details anymore.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1888
        let left_room = client
7ff8adafe9e4atagen2026-10-22 12:30 UTC1889
            .get_room(known_room_id)
7ff8adafe9e4atagen2026-10-22 12:30 UTC1890
            .expect("The sync should have created a room in the invited state");
7ff8adafe9e4atagen2026-10-22 12:30 UTC1891
7ff8adafe9e4atagen2026-10-22 12:30 UTC1892
        assert_eq!(left_room.state(), RoomState::Left);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1893
        assert!(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1894
            client.get_pending_key_bundle_details_for_room(known_room_id).await.unwrap().is_none()
7ff8adafe9e4atagen2026-10-22 12:30 UTC1895
        );
7ff8adafe9e4atagen2026-10-22 12:30 UTC1896
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1897
}