atagen

matrix-rust-sdk

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

Blame: bindings/matrix-sdk-ffi/src/client.rs

7ff8adafe9e4atagen2026-10-22 12:30 UTC1
// Copyright 2025 The Matrix.org Foundation C.I.C.
7ff8adafe9e4atagen2026-10-22 12:30 UTC2
//
7ff8adafe9e4atagen2026-10-22 12:30 UTC3
// Licensed under the Apache License, Version 2.0 (the "License");
7ff8adafe9e4atagen2026-10-22 12:30 UTC4
// you may not use this file except in compliance with the License.
7ff8adafe9e4atagen2026-10-22 12:30 UTC5
// You may obtain a copy of the License at
7ff8adafe9e4atagen2026-10-22 12:30 UTC6
//
7ff8adafe9e4atagen2026-10-22 12:30 UTC7
//     http://www.apache.org/licenses/LICENSE-2.0
7ff8adafe9e4atagen2026-10-22 12:30 UTC8
//
7ff8adafe9e4atagen2026-10-22 12:30 UTC9
// Unless required by applicable law or agreed to in writing, software
7ff8adafe9e4atagen2026-10-22 12:30 UTC10
// distributed under the License is distributed on an "AS IS" BASIS,
7ff8adafe9e4atagen2026-10-22 12:30 UTC11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
7ff8adafe9e4atagen2026-10-22 12:30 UTC12
// See the License for that specific language governing permissions and
7ff8adafe9e4atagen2026-10-22 12:30 UTC13
// limitations under the License.
7ff8adafe9e4atagen2026-10-22 12:30 UTC14
7ff8adafe9e4atagen2026-10-22 12:30 UTC15
use std::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC16
    collections::HashMap,
7ff8adafe9e4atagen2026-10-22 12:30 UTC17
    fmt::Debug,
7ff8adafe9e4atagen2026-10-22 12:30 UTC18
    path::PathBuf,
7ff8adafe9e4atagen2026-10-22 12:30 UTC19
    sync::{Arc, OnceLock},
7ff8adafe9e4atagen2026-10-22 12:30 UTC20
    time::Duration,
7ff8adafe9e4atagen2026-10-22 12:30 UTC21
};
7ff8adafe9e4atagen2026-10-22 12:30 UTC22
7ff8adafe9e4atagen2026-10-22 12:30 UTC23
use anyhow::{Context as _, anyhow};
7ff8adafe9e4atagen2026-10-22 12:30 UTC24
use futures_util::pin_mut;
7ff8adafe9e4atagen2026-10-22 12:30 UTC25
#[cfg(feature = "sqlite")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC26
use matrix_sdk::STATE_STORE_DATABASE_NAME;
7ff8adafe9e4atagen2026-10-22 12:30 UTC27
#[cfg(not(target_family = "wasm"))]
7ff8adafe9e4atagen2026-10-22 12:30 UTC28
use matrix_sdk::media::MediaFileHandle as SdkMediaFileHandle;
7ff8adafe9e4atagen2026-10-22 12:30 UTC29
use matrix_sdk::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC30
    Account, AuthApi, AuthSession, Client as MatrixClient, Error, SessionChange, SessionTokens,
7ff8adafe9e4atagen2026-10-22 12:30 UTC31
    authentication::oauth::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC32
        ClientId, OAuthAuthorizationData, OAuthError as SdkOAuthError, OAuthSession,
7ff8adafe9e4atagen2026-10-22 12:30 UTC33
    },
7ff8adafe9e4atagen2026-10-22 12:30 UTC34
    deserialized_responses::RawAnySyncOrStrippedTimelineEvent,
7ff8adafe9e4atagen2026-10-22 12:30 UTC35
    executor::AbortOnDrop,
7ff8adafe9e4atagen2026-10-22 12:30 UTC36
    media::{MediaFormat, MediaRequestParameters, MediaRetentionPolicy, MediaThumbnailSettings},
7ff8adafe9e4atagen2026-10-22 12:30 UTC37
    ruma::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC38
        EventEncryptionAlgorithm, RoomId, TransactionId, UInt, UserId,
7ff8adafe9e4atagen2026-10-22 12:30 UTC39
        api::client::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC40
            account::request_openid_token,
7ff8adafe9e4atagen2026-10-22 12:30 UTC41
            discovery::get_authorization_server_metadata::v1::Prompt as RumaOAuthPrompt,
7ff8adafe9e4atagen2026-10-22 12:30 UTC42
            push::{EmailPusherData, PusherIds, PusherInit, PusherKind as RumaPusherKind},
7ff8adafe9e4atagen2026-10-22 12:30 UTC43
            room::{Visibility, create_room},
7ff8adafe9e4atagen2026-10-22 12:30 UTC44
            rtc::RtcTransport,
7ff8adafe9e4atagen2026-10-22 12:30 UTC45
            session::get_login_types,
7ff8adafe9e4atagen2026-10-22 12:30 UTC46
            user_directory::search_users,
7ff8adafe9e4atagen2026-10-22 12:30 UTC47
        },
7ff8adafe9e4atagen2026-10-22 12:30 UTC48
        events::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC49
            AnyInitialStateEvent, InitialStateEvent,
7ff8adafe9e4atagen2026-10-22 12:30 UTC50
            room::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC51
                avatar::RoomAvatarEventContent, encryption::RoomEncryptionEventContent,
7ff8adafe9e4atagen2026-10-22 12:30 UTC52
                message::MessageType,
7ff8adafe9e4atagen2026-10-22 12:30 UTC53
            },
7ff8adafe9e4atagen2026-10-22 12:30 UTC54
        },
7ff8adafe9e4atagen2026-10-22 12:30 UTC55
        serde::Raw,
7ff8adafe9e4atagen2026-10-22 12:30 UTC56
    },
7ff8adafe9e4atagen2026-10-22 12:30 UTC57
    sliding_sync::Version as SdkSlidingSyncVersion,
7ff8adafe9e4atagen2026-10-22 12:30 UTC58
    store::RoomLoadSettings as SdkRoomLoadSettings,
7ff8adafe9e4atagen2026-10-22 12:30 UTC59
    sync::Notification,
7ff8adafe9e4atagen2026-10-22 12:30 UTC60
    task_monitor::BackgroundTaskFailureReason,
7ff8adafe9e4atagen2026-10-22 12:30 UTC61
};
7ff8adafe9e4atagen2026-10-22 12:30 UTC62
use matrix_sdk_common::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC63
    SendOutsideWasm, SyncOutsideWasm, cross_process_lock::CrossProcessLockConfig, stream::StreamExt,
7ff8adafe9e4atagen2026-10-22 12:30 UTC64
};
7ff8adafe9e4atagen2026-10-22 12:30 UTC65
use matrix_sdk_ui::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC66
    notification_client::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC67
        NotificationClient as MatrixNotificationClient,
7ff8adafe9e4atagen2026-10-22 12:30 UTC68
        NotificationProcessSetup as MatrixNotificationProcessSetup,
7ff8adafe9e4atagen2026-10-22 12:30 UTC69
    },
7ff8adafe9e4atagen2026-10-22 12:30 UTC70
    spaces::SpaceService as UISpaceService,
7ff8adafe9e4atagen2026-10-22 12:30 UTC71
    unable_to_decrypt_hook::UtdHookManager,
7ff8adafe9e4atagen2026-10-22 12:30 UTC72
};
7ff8adafe9e4atagen2026-10-22 12:30 UTC73
use mime::Mime;
7ff8adafe9e4atagen2026-10-22 12:30 UTC74
use oauth2::Scope;
7ff8adafe9e4atagen2026-10-22 12:30 UTC75
use ruma::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC76
    OwnedDeviceId, OwnedMxcUri, OwnedServerName, RoomAliasId, RoomOrAliasId, ServerName,
7ff8adafe9e4atagen2026-10-22 12:30 UTC77
    api::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC78
        client::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC79
            alias::get_alias,
7ff8adafe9e4atagen2026-10-22 12:30 UTC80
            discovery::get_authorization_server_metadata::v1::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC81
                AccountManagementActionData, DeviceDeleteData, DeviceViewData,
7ff8adafe9e4atagen2026-10-22 12:30 UTC82
            },
7ff8adafe9e4atagen2026-10-22 12:30 UTC83
            profile::{AvatarUrl, DisplayName},
7ff8adafe9e4atagen2026-10-22 12:30 UTC84
            room::create_room::{RoomPowerLevelsContentOverride, v3::CreationContent},
7ff8adafe9e4atagen2026-10-22 12:30 UTC85
            uiaa::{EmailUserIdentifier, UserIdentifier},
7ff8adafe9e4atagen2026-10-22 12:30 UTC86
        },
7ff8adafe9e4atagen2026-10-22 12:30 UTC87
        error::ErrorKind,
7ff8adafe9e4atagen2026-10-22 12:30 UTC88
    },
7ff8adafe9e4atagen2026-10-22 12:30 UTC89
    events::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC90
        AnyMessageLikeEventContent, AnySyncTimelineEvent,
7ff8adafe9e4atagen2026-10-22 12:30 UTC91
        GlobalAccountDataEvent as RumaGlobalAccountDataEvent,
7ff8adafe9e4atagen2026-10-22 12:30 UTC92
        RoomAccountDataEvent as RumaRoomAccountDataEvent,
7ff8adafe9e4atagen2026-10-22 12:30 UTC93
        direct::DirectEventContent,
7ff8adafe9e4atagen2026-10-22 12:30 UTC94
        fully_read::FullyReadEventContent,
7ff8adafe9e4atagen2026-10-22 12:30 UTC95
        identity_server::IdentityServerEventContent,
7ff8adafe9e4atagen2026-10-22 12:30 UTC96
        ignored_user_list::IgnoredUserListEventContent,
7ff8adafe9e4atagen2026-10-22 12:30 UTC97
        key::verification::request::ToDeviceKeyVerificationRequestEvent,
7ff8adafe9e4atagen2026-10-22 12:30 UTC98
        marked_unread::{MarkedUnreadEventContent, UnstableMarkedUnreadEventContent},
7ff8adafe9e4atagen2026-10-22 12:30 UTC99
        push_rules::PushRulesEventContent,
7ff8adafe9e4atagen2026-10-22 12:30 UTC100
        room::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC101
            history_visibility::RoomHistoryVisibilityEventContent,
7ff8adafe9e4atagen2026-10-22 12:30 UTC102
            join_rules::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC103
                AllowRule as RumaAllowRule, JoinRule as RumaJoinRule, RoomJoinRulesEventContent,
7ff8adafe9e4atagen2026-10-22 12:30 UTC104
            },
7ff8adafe9e4atagen2026-10-22 12:30 UTC105
            message::{OriginalSyncRoomMessageEvent, Relation},
7ff8adafe9e4atagen2026-10-22 12:30 UTC106
        },
7ff8adafe9e4atagen2026-10-22 12:30 UTC107
        secret_storage::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC108
            default_key::SecretStorageDefaultKeyEventContent, key::SecretStorageKeyEventContent,
7ff8adafe9e4atagen2026-10-22 12:30 UTC109
        },
7ff8adafe9e4atagen2026-10-22 12:30 UTC110
        tag::TagEventContent,
7ff8adafe9e4atagen2026-10-22 12:30 UTC111
    },
7ff8adafe9e4atagen2026-10-22 12:30 UTC112
    push::{HttpPusherData as RumaHttpPusherData, PushFormat as RumaPushFormat},
7ff8adafe9e4atagen2026-10-22 12:30 UTC113
    room::RoomType,
7ff8adafe9e4atagen2026-10-22 12:30 UTC114
};
7ff8adafe9e4atagen2026-10-22 12:30 UTC115
use serde::{Deserialize, Serialize};
7ff8adafe9e4atagen2026-10-22 12:30 UTC116
use serde_json::{Value, json};
7ff8adafe9e4atagen2026-10-22 12:30 UTC117
use tokio::sync::broadcast::error::RecvError;
7ff8adafe9e4atagen2026-10-22 12:30 UTC118
use tracing::{debug, error, warn};
7ff8adafe9e4atagen2026-10-22 12:30 UTC119
use url::Url;
7ff8adafe9e4atagen2026-10-22 12:30 UTC120
7ff8adafe9e4atagen2026-10-22 12:30 UTC121
use super::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC122
    room::{Room, room_info::RoomInfo},
7ff8adafe9e4atagen2026-10-22 12:30 UTC123
    session_verification::SessionVerificationController,
7ff8adafe9e4atagen2026-10-22 12:30 UTC124
};
7ff8adafe9e4atagen2026-10-22 12:30 UTC125
use crate::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC126
    ClientError,
7ff8adafe9e4atagen2026-10-22 12:30 UTC127
    authentication::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC128
        HomeserverLoginDetails, OAuthConfiguration, OAuthError, SsoError, SsoHandler,
7ff8adafe9e4atagen2026-10-22 12:30 UTC129
    },
7ff8adafe9e4atagen2026-10-22 12:30 UTC130
    client,
7ff8adafe9e4atagen2026-10-22 12:30 UTC131
    encryption::Encryption,
7ff8adafe9e4atagen2026-10-22 12:30 UTC132
    live_locations_observer::BeaconInfoUpdate,
7ff8adafe9e4atagen2026-10-22 12:30 UTC133
    notification::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC134
        NotificationClient, NotificationEvent, NotificationItem, NotificationRoomInfo,
7ff8adafe9e4atagen2026-10-22 12:30 UTC135
        NotificationSenderInfo,
7ff8adafe9e4atagen2026-10-22 12:30 UTC136
    },
7ff8adafe9e4atagen2026-10-22 12:30 UTC137
    notification_settings::NotificationSettings,
7ff8adafe9e4atagen2026-10-22 12:30 UTC138
    qr_code::{GrantLoginWithQrCodeHandler, LoginWithQrCodeHandler},
7ff8adafe9e4atagen2026-10-22 12:30 UTC139
    room::{RoomHistoryVisibility, RoomInfoListener, RoomSendQueueUpdate},
7ff8adafe9e4atagen2026-10-22 12:30 UTC140
    room_directory_search::RoomDirectorySearch,
7ff8adafe9e4atagen2026-10-22 12:30 UTC141
    room_preview::RoomPreview,
7ff8adafe9e4atagen2026-10-22 12:30 UTC142
    ruma::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC143
        AccountDataEvent, AccountDataEventType, AuthData, InviteAvatars, MediaPreviewConfig,
7ff8adafe9e4atagen2026-10-22 12:30 UTC144
        MediaPreviews, MediaSource, RoomAccountDataEvent, RoomAccountDataEventType,
7ff8adafe9e4atagen2026-10-22 12:30 UTC145
    },
7ff8adafe9e4atagen2026-10-22 12:30 UTC146
    runtime::get_runtime_handle,
7ff8adafe9e4atagen2026-10-22 12:30 UTC147
    spaces::SpaceService,
7ff8adafe9e4atagen2026-10-22 12:30 UTC148
    sync_service::{SyncService, SyncServiceBuilder},
7ff8adafe9e4atagen2026-10-22 12:30 UTC149
    sync_v2::{SyncListenerV2, SyncResponseV2, SyncSettingsV2},
7ff8adafe9e4atagen2026-10-22 12:30 UTC150
    task_handle::TaskHandle,
7ff8adafe9e4atagen2026-10-22 12:30 UTC151
    utd::{UnableToDecryptDelegate, UtdHook},
7ff8adafe9e4atagen2026-10-22 12:30 UTC152
    utils::AsyncRuntimeDropped,
7ff8adafe9e4atagen2026-10-22 12:30 UTC153
};
7ff8adafe9e4atagen2026-10-22 12:30 UTC154
7ff8adafe9e4atagen2026-10-22 12:30 UTC155
#[derive(Clone, uniffi::Record)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC156
pub struct PusherIdentifiers {
7ff8adafe9e4atagen2026-10-22 12:30 UTC157
    pub pushkey: String,
7ff8adafe9e4atagen2026-10-22 12:30 UTC158
    pub app_id: String,
7ff8adafe9e4atagen2026-10-22 12:30 UTC159
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC160
7ff8adafe9e4atagen2026-10-22 12:30 UTC161
impl From<PusherIdentifiers> for PusherIds {
7ff8adafe9e4atagen2026-10-22 12:30 UTC162
    fn from(value: PusherIdentifiers) -> Self {
7ff8adafe9e4atagen2026-10-22 12:30 UTC163
        Self::new(value.pushkey, value.app_id)
7ff8adafe9e4atagen2026-10-22 12:30 UTC164
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC165
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC166
7ff8adafe9e4atagen2026-10-22 12:30 UTC167
#[derive(Clone, uniffi::Record)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC168
pub struct HttpPusherData {
7ff8adafe9e4atagen2026-10-22 12:30 UTC169
    pub url: String,
7ff8adafe9e4atagen2026-10-22 12:30 UTC170
    pub format: Option<PushFormat>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC171
    pub default_payload: Option<String>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC172
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC173
7ff8adafe9e4atagen2026-10-22 12:30 UTC174
#[derive(Clone, uniffi::Enum)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC175
pub enum PusherKind {
7ff8adafe9e4atagen2026-10-22 12:30 UTC176
    Http { data: HttpPusherData },
7ff8adafe9e4atagen2026-10-22 12:30 UTC177
    Email,
7ff8adafe9e4atagen2026-10-22 12:30 UTC178
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC179
7ff8adafe9e4atagen2026-10-22 12:30 UTC180
impl TryFrom<PusherKind> for RumaPusherKind {
7ff8adafe9e4atagen2026-10-22 12:30 UTC181
    type Error = anyhow::Error;
7ff8adafe9e4atagen2026-10-22 12:30 UTC182
7ff8adafe9e4atagen2026-10-22 12:30 UTC183
    fn try_from(value: PusherKind) -> anyhow::Result<Self> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC184
        match value {
7ff8adafe9e4atagen2026-10-22 12:30 UTC185
            PusherKind::Http { data } => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC186
                let mut ruma_data = RumaHttpPusherData::new(data.url);
7ff8adafe9e4atagen2026-10-22 12:30 UTC187
                if let Some(payload) = data.default_payload {
7ff8adafe9e4atagen2026-10-22 12:30 UTC188
                    let json: Value = serde_json::from_str(&payload)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC189
                    ruma_data.data.insert("default_payload".to_owned(), json);
7ff8adafe9e4atagen2026-10-22 12:30 UTC190
                }
7ff8adafe9e4atagen2026-10-22 12:30 UTC191
                ruma_data.format = data.format.map(Into::into);
7ff8adafe9e4atagen2026-10-22 12:30 UTC192
                Ok(Self::Http(ruma_data))
7ff8adafe9e4atagen2026-10-22 12:30 UTC193
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC194
            PusherKind::Email => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC195
                let ruma_data = EmailPusherData::new();
7ff8adafe9e4atagen2026-10-22 12:30 UTC196
                Ok(Self::Email(ruma_data))
7ff8adafe9e4atagen2026-10-22 12:30 UTC197
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC198
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC199
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC200
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC201
7ff8adafe9e4atagen2026-10-22 12:30 UTC202
#[derive(Clone, uniffi::Enum)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC203
pub enum PushFormat {
7ff8adafe9e4atagen2026-10-22 12:30 UTC204
    EventIdOnly,
7ff8adafe9e4atagen2026-10-22 12:30 UTC205
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC206
7ff8adafe9e4atagen2026-10-22 12:30 UTC207
impl From<PushFormat> for RumaPushFormat {
7ff8adafe9e4atagen2026-10-22 12:30 UTC208
    fn from(value: PushFormat) -> Self {
7ff8adafe9e4atagen2026-10-22 12:30 UTC209
        match value {
7ff8adafe9e4atagen2026-10-22 12:30 UTC210
            client::PushFormat::EventIdOnly => Self::EventIdOnly,
7ff8adafe9e4atagen2026-10-22 12:30 UTC211
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC212
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC213
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC214
7ff8adafe9e4atagen2026-10-22 12:30 UTC215
#[matrix_sdk_ffi_macros::export(callback_interface)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC216
pub trait ClientDelegate: SyncOutsideWasm + SendOutsideWasm {
7ff8adafe9e4atagen2026-10-22 12:30 UTC217
    /// A callback invoked whenever the SDK runs into an unknown token error.
7ff8adafe9e4atagen2026-10-22 12:30 UTC218
    fn did_receive_auth_error(&self, is_soft_logout: bool);
7ff8adafe9e4atagen2026-10-22 12:30 UTC219
7ff8adafe9e4atagen2026-10-22 12:30 UTC220
    /// A callback invoked when a background task registered with the client's
7ff8adafe9e4atagen2026-10-22 12:30 UTC221
    /// task monitor encounters an error.
7ff8adafe9e4atagen2026-10-22 12:30 UTC222
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC223
    /// Can default to an empty implementation, if the embedder doesn't care
7ff8adafe9e4atagen2026-10-22 12:30 UTC224
    /// about handling background jobs errors.
7ff8adafe9e4atagen2026-10-22 12:30 UTC225
    fn on_background_task_error_report(
7ff8adafe9e4atagen2026-10-22 12:30 UTC226
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC227
        task_name: String,
7ff8adafe9e4atagen2026-10-22 12:30 UTC228
        error: BackgroundTaskFailureReason,
7ff8adafe9e4atagen2026-10-22 12:30 UTC229
    );
7ff8adafe9e4atagen2026-10-22 12:30 UTC230
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC231
7ff8adafe9e4atagen2026-10-22 12:30 UTC232
#[matrix_sdk_ffi_macros::export(callback_interface)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC233
pub trait ClientSessionDelegate: SyncOutsideWasm + SendOutsideWasm {
7ff8adafe9e4atagen2026-10-22 12:30 UTC234
    fn retrieve_session_from_keychain(&self, user_id: String) -> Result<Session, ClientError>;
7ff8adafe9e4atagen2026-10-22 12:30 UTC235
    fn save_session_in_keychain(&self, session: Session);
7ff8adafe9e4atagen2026-10-22 12:30 UTC236
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC237
7ff8adafe9e4atagen2026-10-22 12:30 UTC238
#[matrix_sdk_ffi_macros::export(callback_interface)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC239
pub trait ProgressWatcher: SyncOutsideWasm + SendOutsideWasm {
7ff8adafe9e4atagen2026-10-22 12:30 UTC240
    fn transmission_progress(&self, progress: TransmissionProgress);
7ff8adafe9e4atagen2026-10-22 12:30 UTC241
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC242
7ff8adafe9e4atagen2026-10-22 12:30 UTC243
/// A listener to the global (client-wide) update reporter of the send queue.
7ff8adafe9e4atagen2026-10-22 12:30 UTC244
#[matrix_sdk_ffi_macros::export(callback_interface)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC245
pub trait SendQueueRoomUpdateListener: SyncOutsideWasm + SendOutsideWasm {
7ff8adafe9e4atagen2026-10-22 12:30 UTC246
    /// Called every time the send queue emits an update for a given room.
7ff8adafe9e4atagen2026-10-22 12:30 UTC247
    fn on_update(&self, room_id: String, update: RoomSendQueueUpdate);
7ff8adafe9e4atagen2026-10-22 12:30 UTC248
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC249
7ff8adafe9e4atagen2026-10-22 12:30 UTC250
/// A listener to the global (client-wide) error reporter of the send queue.
7ff8adafe9e4atagen2026-10-22 12:30 UTC251
#[matrix_sdk_ffi_macros::export(callback_interface)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC252
pub trait SendQueueRoomErrorListener: SyncOutsideWasm + SendOutsideWasm {
7ff8adafe9e4atagen2026-10-22 12:30 UTC253
    /// Called every time the send queue has ran into an error for a given room,
7ff8adafe9e4atagen2026-10-22 12:30 UTC254
    /// which will disable the send queue for that particular room.
7ff8adafe9e4atagen2026-10-22 12:30 UTC255
    fn on_error(&self, room_id: String, error: ClientError);
7ff8adafe9e4atagen2026-10-22 12:30 UTC256
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC257
7ff8adafe9e4atagen2026-10-22 12:30 UTC258
/// A listener for changes of global account data events.
7ff8adafe9e4atagen2026-10-22 12:30 UTC259
#[matrix_sdk_ffi_macros::export(callback_interface)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC260
pub trait AccountDataListener: SyncOutsideWasm + SendOutsideWasm {
7ff8adafe9e4atagen2026-10-22 12:30 UTC261
    /// Called when a global account data event has changed.
7ff8adafe9e4atagen2026-10-22 12:30 UTC262
    fn on_change(&self, event: AccountDataEvent);
7ff8adafe9e4atagen2026-10-22 12:30 UTC263
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC264
7ff8adafe9e4atagen2026-10-22 12:30 UTC265
/// A listener for duplicate key upload errors triggered by requests to
7ff8adafe9e4atagen2026-10-22 12:30 UTC266
/// /keys/upload.
7ff8adafe9e4atagen2026-10-22 12:30 UTC267
#[matrix_sdk_ffi_macros::export(callback_interface)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC268
pub trait DuplicateKeyUploadErrorListener: SyncOutsideWasm + SendOutsideWasm {
7ff8adafe9e4atagen2026-10-22 12:30 UTC269
    /// Called once when uploading keys fails.
7ff8adafe9e4atagen2026-10-22 12:30 UTC270
    fn on_duplicate_key_upload_error(&self, message: Option<DuplicateOneTimeKeyErrorMessage>);
7ff8adafe9e4atagen2026-10-22 12:30 UTC271
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC272
7ff8adafe9e4atagen2026-10-22 12:30 UTC273
/// A listener for the current user's client-wide beacon_info updates.
7ff8adafe9e4atagen2026-10-22 12:30 UTC274
#[matrix_sdk_ffi_macros::export(callback_interface)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC275
pub trait BeaconInfoListener: SyncOutsideWasm + SendOutsideWasm {
7ff8adafe9e4atagen2026-10-22 12:30 UTC276
    /// Called whenever the current user's beacon_info changes in any room.
7ff8adafe9e4atagen2026-10-22 12:30 UTC277
    fn on_update(&self, update: BeaconInfoUpdate);
7ff8adafe9e4atagen2026-10-22 12:30 UTC278
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC279
7ff8adafe9e4atagen2026-10-22 12:30 UTC280
/// Information about the old and new key that caused a duplicate key upload
7ff8adafe9e4atagen2026-10-22 12:30 UTC281
/// error in /keys/upload.
7ff8adafe9e4atagen2026-10-22 12:30 UTC282
#[derive(uniffi::Record)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC283
pub struct DuplicateOneTimeKeyErrorMessage {
7ff8adafe9e4atagen2026-10-22 12:30 UTC284
    /// The previously uploaded one-time key, encoded as unpadded base64.
7ff8adafe9e4atagen2026-10-22 12:30 UTC285
    pub old_key: String,
7ff8adafe9e4atagen2026-10-22 12:30 UTC286
    /// The one-time key we attempted to upload, encoded as unpadded base64
7ff8adafe9e4atagen2026-10-22 12:30 UTC287
    pub new_key: String,
7ff8adafe9e4atagen2026-10-22 12:30 UTC288
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC289
7ff8adafe9e4atagen2026-10-22 12:30 UTC290
impl From<matrix_sdk::encryption::DuplicateOneTimeKeyErrorMessage>
7ff8adafe9e4atagen2026-10-22 12:30 UTC291
    for DuplicateOneTimeKeyErrorMessage
7ff8adafe9e4atagen2026-10-22 12:30 UTC292
{
7ff8adafe9e4atagen2026-10-22 12:30 UTC293
    fn from(value: matrix_sdk::encryption::DuplicateOneTimeKeyErrorMessage) -> Self {
7ff8adafe9e4atagen2026-10-22 12:30 UTC294
        Self { old_key: value.old_key.to_base64(), new_key: value.new_key.to_base64() }
7ff8adafe9e4atagen2026-10-22 12:30 UTC295
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC296
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC297
7ff8adafe9e4atagen2026-10-22 12:30 UTC298
/// A listener for changes of room account data events.
7ff8adafe9e4atagen2026-10-22 12:30 UTC299
#[matrix_sdk_ffi_macros::export(callback_interface)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC300
pub trait RoomAccountDataListener: SyncOutsideWasm + SendOutsideWasm {
7ff8adafe9e4atagen2026-10-22 12:30 UTC301
    /// Called when a room account data event was changed.
7ff8adafe9e4atagen2026-10-22 12:30 UTC302
    fn on_change(&self, event: RoomAccountDataEvent, room_id: String);
7ff8adafe9e4atagen2026-10-22 12:30 UTC303
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC304
7ff8adafe9e4atagen2026-10-22 12:30 UTC305
/// A listener for notifications generated from sync responses.
7ff8adafe9e4atagen2026-10-22 12:30 UTC306
///
7ff8adafe9e4atagen2026-10-22 12:30 UTC307
/// This is called during sync for each event that triggers a notification
7ff8adafe9e4atagen2026-10-22 12:30 UTC308
/// based on the user's push rules.
7ff8adafe9e4atagen2026-10-22 12:30 UTC309
#[matrix_sdk_ffi_macros::export(callback_interface)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC310
pub trait SyncNotificationListener: SyncOutsideWasm + SendOutsideWasm {
7ff8adafe9e4atagen2026-10-22 12:30 UTC311
    /// Called when a notifying event is received during sync.
7ff8adafe9e4atagen2026-10-22 12:30 UTC312
    fn on_notification(&self, notification: NotificationItem, room_id: String);
7ff8adafe9e4atagen2026-10-22 12:30 UTC313
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC314
7ff8adafe9e4atagen2026-10-22 12:30 UTC315
#[derive(Clone, Copy, uniffi::Record)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC316
pub struct TransmissionProgress {
7ff8adafe9e4atagen2026-10-22 12:30 UTC317
    pub current: u64,
7ff8adafe9e4atagen2026-10-22 12:30 UTC318
    pub total: u64,
7ff8adafe9e4atagen2026-10-22 12:30 UTC319
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC320
7ff8adafe9e4atagen2026-10-22 12:30 UTC321
impl From<matrix_sdk::TransmissionProgress> for TransmissionProgress {
7ff8adafe9e4atagen2026-10-22 12:30 UTC322
    fn from(value: matrix_sdk::TransmissionProgress) -> Self {
7ff8adafe9e4atagen2026-10-22 12:30 UTC323
        Self {
7ff8adafe9e4atagen2026-10-22 12:30 UTC324
            current: value.current.try_into().unwrap_or(u64::MAX),
7ff8adafe9e4atagen2026-10-22 12:30 UTC325
            total: value.total.try_into().unwrap_or(u64::MAX),
7ff8adafe9e4atagen2026-10-22 12:30 UTC326
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC327
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC328
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC329
7ff8adafe9e4atagen2026-10-22 12:30 UTC330
#[derive(Clone, uniffi::Record)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC331
pub struct OpenIdToken {
7ff8adafe9e4atagen2026-10-22 12:30 UTC332
    pub access_token: String,
7ff8adafe9e4atagen2026-10-22 12:30 UTC333
    pub token_type: String,
7ff8adafe9e4atagen2026-10-22 12:30 UTC334
    pub matrix_server_name: String,
7ff8adafe9e4atagen2026-10-22 12:30 UTC335
    pub expires_in_seconds: u64,
7ff8adafe9e4atagen2026-10-22 12:30 UTC336
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC337
7ff8adafe9e4atagen2026-10-22 12:30 UTC338
impl From<request_openid_token::v3::Response> for OpenIdToken {
7ff8adafe9e4atagen2026-10-22 12:30 UTC339
    fn from(value: request_openid_token::v3::Response) -> Self {
7ff8adafe9e4atagen2026-10-22 12:30 UTC340
        Self {
7ff8adafe9e4atagen2026-10-22 12:30 UTC341
            access_token: value.access_token,
7ff8adafe9e4atagen2026-10-22 12:30 UTC342
            token_type: serde_json::to_value(&value.token_type)
7ff8adafe9e4atagen2026-10-22 12:30 UTC343
                .ok()
7ff8adafe9e4atagen2026-10-22 12:30 UTC344
                .and_then(|value| value.as_str().map(ToOwned::to_owned))
7ff8adafe9e4atagen2026-10-22 12:30 UTC345
                .unwrap_or_else(|| format!("{:?}", value.token_type)),
7ff8adafe9e4atagen2026-10-22 12:30 UTC346
            matrix_server_name: value.matrix_server_name.to_string(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC347
            expires_in_seconds: value.expires_in.as_secs(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC348
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC349
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC350
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC351
7ff8adafe9e4atagen2026-10-22 12:30 UTC352
struct ClientDelegateData {
7ff8adafe9e4atagen2026-10-22 12:30 UTC353
    /// The delegate itself, that will receive the callbacks.
7ff8adafe9e4atagen2026-10-22 12:30 UTC354
    delegate: Arc<dyn ClientDelegate>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC355
7ff8adafe9e4atagen2026-10-22 12:30 UTC356
    // The background task error listener task, that will forward errors occurring in background
7ff8adafe9e4atagen2026-10-22 12:30 UTC357
    // jobs to the delegate.
7ff8adafe9e4atagen2026-10-22 12:30 UTC358
    _background_error_listener_task: Arc<AbortOnDrop<()>>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC359
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC360
7ff8adafe9e4atagen2026-10-22 12:30 UTC361
#[derive(uniffi::Object)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC362
pub struct Client {
7ff8adafe9e4atagen2026-10-22 12:30 UTC363
    pub(crate) inner: AsyncRuntimeDropped<MatrixClient>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC364
7ff8adafe9e4atagen2026-10-22 12:30 UTC365
    delegate_data: OnceLock<ClientDelegateData>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC366
7ff8adafe9e4atagen2026-10-22 12:30 UTC367
    pub(crate) utd_hook_manager: OnceLock<Arc<UtdHookManager>>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC368
7ff8adafe9e4atagen2026-10-22 12:30 UTC369
    session_verification_controller:
7ff8adafe9e4atagen2026-10-22 12:30 UTC370
        Arc<tokio::sync::RwLock<Option<SessionVerificationController>>>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC371
7ff8adafe9e4atagen2026-10-22 12:30 UTC372
    /// The path to the directory where the state store and the crypto store are
7ff8adafe9e4atagen2026-10-22 12:30 UTC373
    /// located, if the `Client` instance has been built with a store (either
7ff8adafe9e4atagen2026-10-22 12:30 UTC374
    /// SQLite or IndexedDB).
7ff8adafe9e4atagen2026-10-22 12:30 UTC375
    #[cfg_attr(not(feature = "sqlite"), allow(unused))]
7ff8adafe9e4atagen2026-10-22 12:30 UTC376
    store_path: Option<PathBuf>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC377
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC378
7ff8adafe9e4atagen2026-10-22 12:30 UTC379
impl Client {
7ff8adafe9e4atagen2026-10-22 12:30 UTC380
    pub async fn new(
7ff8adafe9e4atagen2026-10-22 12:30 UTC381
        sdk_client: MatrixClient,
7ff8adafe9e4atagen2026-10-22 12:30 UTC382
        session_delegate: Option<Arc<dyn ClientSessionDelegate>>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC383
        store_path: Option<PathBuf>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC384
    ) -> Result<Self, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC385
        let session_verification_controller: Arc<
7ff8adafe9e4atagen2026-10-22 12:30 UTC386
            tokio::sync::RwLock<Option<SessionVerificationController>>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC387
        > = Default::default();
7ff8adafe9e4atagen2026-10-22 12:30 UTC388
        let controller = session_verification_controller.clone();
7ff8adafe9e4atagen2026-10-22 12:30 UTC389
        sdk_client.add_event_handler(
7ff8adafe9e4atagen2026-10-22 12:30 UTC390
            move |event: ToDeviceKeyVerificationRequestEvent| async move {
7ff8adafe9e4atagen2026-10-22 12:30 UTC391
                if let Some(session_verification_controller) = &*controller.clone().read().await {
7ff8adafe9e4atagen2026-10-22 12:30 UTC392
                    session_verification_controller
7ff8adafe9e4atagen2026-10-22 12:30 UTC393
                        .process_incoming_verification_request(
7ff8adafe9e4atagen2026-10-22 12:30 UTC394
                            &event.sender,
7ff8adafe9e4atagen2026-10-22 12:30 UTC395
                            event.content.transaction_id,
7ff8adafe9e4atagen2026-10-22 12:30 UTC396
                        )
7ff8adafe9e4atagen2026-10-22 12:30 UTC397
                        .await;
7ff8adafe9e4atagen2026-10-22 12:30 UTC398
                }
7ff8adafe9e4atagen2026-10-22 12:30 UTC399
            },
7ff8adafe9e4atagen2026-10-22 12:30 UTC400
        );
7ff8adafe9e4atagen2026-10-22 12:30 UTC401
7ff8adafe9e4atagen2026-10-22 12:30 UTC402
        let controller = session_verification_controller.clone();
7ff8adafe9e4atagen2026-10-22 12:30 UTC403
        sdk_client.add_event_handler(move |event: OriginalSyncRoomMessageEvent| async move {
7ff8adafe9e4atagen2026-10-22 12:30 UTC404
            if let MessageType::VerificationRequest(_) = &event.content.msgtype
7ff8adafe9e4atagen2026-10-22 12:30 UTC405
                && let Some(session_verification_controller) = &*controller.clone().read().await
7ff8adafe9e4atagen2026-10-22 12:30 UTC406
            {
7ff8adafe9e4atagen2026-10-22 12:30 UTC407
                session_verification_controller
7ff8adafe9e4atagen2026-10-22 12:30 UTC408
                    .process_incoming_verification_request(&event.sender, event.event_id)
7ff8adafe9e4atagen2026-10-22 12:30 UTC409
                    .await;
7ff8adafe9e4atagen2026-10-22 12:30 UTC410
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC411
        });
7ff8adafe9e4atagen2026-10-22 12:30 UTC412
7ff8adafe9e4atagen2026-10-22 12:30 UTC413
        let store_mode = sdk_client.cross_process_lock_config();
7ff8adafe9e4atagen2026-10-22 12:30 UTC414
7ff8adafe9e4atagen2026-10-22 12:30 UTC415
        let client = Client {
7ff8adafe9e4atagen2026-10-22 12:30 UTC416
            inner: AsyncRuntimeDropped::new(sdk_client.clone()),
7ff8adafe9e4atagen2026-10-22 12:30 UTC417
            delegate_data: OnceLock::new(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC418
            utd_hook_manager: OnceLock::new(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC419
            session_verification_controller,
7ff8adafe9e4atagen2026-10-22 12:30 UTC420
            store_path,
7ff8adafe9e4atagen2026-10-22 12:30 UTC421
        };
7ff8adafe9e4atagen2026-10-22 12:30 UTC422
7ff8adafe9e4atagen2026-10-22 12:30 UTC423
        match store_mode {
7ff8adafe9e4atagen2026-10-22 12:30 UTC424
            CrossProcessLockConfig::MultiProcess { holder_name } => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC425
                if session_delegate.is_none() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC426
                    return Err(anyhow::anyhow!(
7ff8adafe9e4atagen2026-10-22 12:30 UTC427
                        "missing session delegates with multi-process lock configuration"
7ff8adafe9e4atagen2026-10-22 12:30 UTC428
                    ))?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC429
                }
7ff8adafe9e4atagen2026-10-22 12:30 UTC430
                client.inner.oauth().enable_cross_process_refresh_lock(holder_name.clone()).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC431
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC432
            CrossProcessLockConfig::SingleProcess => {}
7ff8adafe9e4atagen2026-10-22 12:30 UTC433
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC434
7ff8adafe9e4atagen2026-10-22 12:30 UTC435
        if let Some(session_delegate) = session_delegate {
7ff8adafe9e4atagen2026-10-22 12:30 UTC436
            client.inner.set_session_callbacks(
7ff8adafe9e4atagen2026-10-22 12:30 UTC437
                {
7ff8adafe9e4atagen2026-10-22 12:30 UTC438
                    let session_delegate = session_delegate.clone();
7ff8adafe9e4atagen2026-10-22 12:30 UTC439
                    Box::new(move |client| {
7ff8adafe9e4atagen2026-10-22 12:30 UTC440
                        let session_delegate = session_delegate.clone();
7ff8adafe9e4atagen2026-10-22 12:30 UTC441
                        let user_id = client.user_id().context("user isn't logged in")?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC442
                        Ok(Self::retrieve_session(session_delegate, user_id)?)
7ff8adafe9e4atagen2026-10-22 12:30 UTC443
                    })
7ff8adafe9e4atagen2026-10-22 12:30 UTC444
                },
7ff8adafe9e4atagen2026-10-22 12:30 UTC445
                {
7ff8adafe9e4atagen2026-10-22 12:30 UTC446
                    let session_delegate = session_delegate.clone();
7ff8adafe9e4atagen2026-10-22 12:30 UTC447
                    Box::new(move |client| {
7ff8adafe9e4atagen2026-10-22 12:30 UTC448
                        let session_delegate = session_delegate.clone();
7ff8adafe9e4atagen2026-10-22 12:30 UTC449
                        Ok(Self::save_session(session_delegate, client)?)
7ff8adafe9e4atagen2026-10-22 12:30 UTC450
                    })
7ff8adafe9e4atagen2026-10-22 12:30 UTC451
                },
7ff8adafe9e4atagen2026-10-22 12:30 UTC452
            )?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC453
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC454
7ff8adafe9e4atagen2026-10-22 12:30 UTC455
        Ok(client)
7ff8adafe9e4atagen2026-10-22 12:30 UTC456
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC457
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC458
7ff8adafe9e4atagen2026-10-22 12:30 UTC459
#[matrix_sdk_ffi_macros::export]
7ff8adafe9e4atagen2026-10-22 12:30 UTC460
impl Client {
7ff8adafe9e4atagen2026-10-22 12:30 UTC461
    /// Perform database optimizations if any are available, i.e. vacuuming in
7ff8adafe9e4atagen2026-10-22 12:30 UTC462
    /// SQLite.
7ff8adafe9e4atagen2026-10-22 12:30 UTC463
    pub async fn optimize_stores(&self) -> Result<(), ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC464
        Ok(self.inner.optimize_stores().await?)
7ff8adafe9e4atagen2026-10-22 12:30 UTC465
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC466
7ff8adafe9e4atagen2026-10-22 12:30 UTC467
    /// Pause the client for background suspension.
7ff8adafe9e4atagen2026-10-22 12:30 UTC468
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC469
    /// This method:
7ff8adafe9e4atagen2026-10-22 12:30 UTC470
    /// 1. Disables all send queues (prevents new message sends).
7ff8adafe9e4atagen2026-10-22 12:30 UTC471
    /// 2. Pauses all database stores, waiting for in-flight operations and
7ff8adafe9e4atagen2026-10-22 12:30 UTC472
    ///    releasing all connections and file locks.
7ff8adafe9e4atagen2026-10-22 12:30 UTC473
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC474
    /// Call [`Client::resume()`] when the app returns to the foreground.
7ff8adafe9e4atagen2026-10-22 12:30 UTC475
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC476
    /// # iOS
7ff8adafe9e4atagen2026-10-22 12:30 UTC477
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC478
    /// Call this before the app is suspended to avoid `0xdead10cc` kills.
7ff8adafe9e4atagen2026-10-22 12:30 UTC479
    /// Typically called from
7ff8adafe9e4atagen2026-10-22 12:30 UTC480
    /// [`applicationDidEnterBackground`](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/applicationdidenterbackground(_:))
7ff8adafe9e4atagen2026-10-22 12:30 UTC481
    /// or an equivalent SwiftUI lifecycle event, *after* stopping the
7ff8adafe9e4atagen2026-10-22 12:30 UTC482
    /// `matrix_sdk_ui::sync_service::SyncService`.
7ff8adafe9e4atagen2026-10-22 12:30 UTC483
    pub async fn pause(&self) -> Result<(), ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC484
        Ok(self.inner.pause().await?)
7ff8adafe9e4atagen2026-10-22 12:30 UTC485
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC486
7ff8adafe9e4atagen2026-10-22 12:30 UTC487
    /// Resume the client after a [`Client::pause()`].
7ff8adafe9e4atagen2026-10-22 12:30 UTC488
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC489
    /// Re-acquires store resources and re-enables send queues.
7ff8adafe9e4atagen2026-10-22 12:30 UTC490
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC491
    /// If your app stopped the `matrix_sdk_ui::sync_service::SyncService`
7ff8adafe9e4atagen2026-10-22 12:30 UTC492
    /// before pausing, restart it separately as appropriate for your app
7ff8adafe9e4atagen2026-10-22 12:30 UTC493
    /// lifecycle.
7ff8adafe9e4atagen2026-10-22 12:30 UTC494
    pub async fn resume(&self) -> Result<(), ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC495
        Ok(self.inner.resume().await?)
7ff8adafe9e4atagen2026-10-22 12:30 UTC496
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC497
7ff8adafe9e4atagen2026-10-22 12:30 UTC498
    /// Returns the sizes of the existing stores, if known.
7ff8adafe9e4atagen2026-10-22 12:30 UTC499
    pub async fn get_store_sizes(&self) -> Result<StoreSizes, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC500
        Ok(self.inner.get_store_sizes().await?.into())
7ff8adafe9e4atagen2026-10-22 12:30 UTC501
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC502
7ff8adafe9e4atagen2026-10-22 12:30 UTC503
    /// Information about login options for the client's homeserver.
7ff8adafe9e4atagen2026-10-22 12:30 UTC504
    pub async fn homeserver_login_details(&self) -> Arc<HomeserverLoginDetails> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC505
        let oauth = self.inner.oauth();
7ff8adafe9e4atagen2026-10-22 12:30 UTC506
        let (supports_oauth_login, supported_oauth_prompts) = match oauth.server_metadata().await {
7ff8adafe9e4atagen2026-10-22 12:30 UTC507
            Ok(metadata) => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC508
                let prompts =
7ff8adafe9e4atagen2026-10-22 12:30 UTC509
                    metadata.prompt_values_supported.into_iter().map(Into::into).collect();
7ff8adafe9e4atagen2026-10-22 12:30 UTC510
7ff8adafe9e4atagen2026-10-22 12:30 UTC511
                (true, prompts)
7ff8adafe9e4atagen2026-10-22 12:30 UTC512
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC513
            Err(error) => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC514
                error!("Failed to fetch OAuth provider metadata: {error}");
7ff8adafe9e4atagen2026-10-22 12:30 UTC515
                (false, Default::default())
7ff8adafe9e4atagen2026-10-22 12:30 UTC516
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC517
        };
7ff8adafe9e4atagen2026-10-22 12:30 UTC518
7ff8adafe9e4atagen2026-10-22 12:30 UTC519
        let login_types = self.inner.matrix_auth().get_login_types().await.ok();
7ff8adafe9e4atagen2026-10-22 12:30 UTC520
        let supports_password_login = login_types
7ff8adafe9e4atagen2026-10-22 12:30 UTC521
            .as_ref()
7ff8adafe9e4atagen2026-10-22 12:30 UTC522
            .map(|login_types| {
7ff8adafe9e4atagen2026-10-22 12:30 UTC523
                login_types.flows.iter().any(|login_type| {
7ff8adafe9e4atagen2026-10-22 12:30 UTC524
                    matches!(login_type, get_login_types::v3::LoginType::Password(_))
7ff8adafe9e4atagen2026-10-22 12:30 UTC525
                })
7ff8adafe9e4atagen2026-10-22 12:30 UTC526
            })
7ff8adafe9e4atagen2026-10-22 12:30 UTC527
            .unwrap_or(false);
7ff8adafe9e4atagen2026-10-22 12:30 UTC528
        let supports_sso_login = login_types
7ff8adafe9e4atagen2026-10-22 12:30 UTC529
            .as_ref()
7ff8adafe9e4atagen2026-10-22 12:30 UTC530
            .map(|login_types| {
7ff8adafe9e4atagen2026-10-22 12:30 UTC531
                login_types
7ff8adafe9e4atagen2026-10-22 12:30 UTC532
                    .flows
7ff8adafe9e4atagen2026-10-22 12:30 UTC533
                    .iter()
7ff8adafe9e4atagen2026-10-22 12:30 UTC534
                    .any(|login_type| matches!(login_type, get_login_types::v3::LoginType::Sso(_)))
7ff8adafe9e4atagen2026-10-22 12:30 UTC535
            })
7ff8adafe9e4atagen2026-10-22 12:30 UTC536
            .unwrap_or(false);
7ff8adafe9e4atagen2026-10-22 12:30 UTC537
        let sliding_sync_version = self.sliding_sync_version();
7ff8adafe9e4atagen2026-10-22 12:30 UTC538
7ff8adafe9e4atagen2026-10-22 12:30 UTC539
        Arc::new(HomeserverLoginDetails {
7ff8adafe9e4atagen2026-10-22 12:30 UTC540
            url: self.homeserver(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC541
            sliding_sync_version,
7ff8adafe9e4atagen2026-10-22 12:30 UTC542
            supports_oauth_login,
7ff8adafe9e4atagen2026-10-22 12:30 UTC543
            supported_oauth_prompts,
7ff8adafe9e4atagen2026-10-22 12:30 UTC544
            supports_sso_login,
7ff8adafe9e4atagen2026-10-22 12:30 UTC545
            supports_password_login,
7ff8adafe9e4atagen2026-10-22 12:30 UTC546
        })
7ff8adafe9e4atagen2026-10-22 12:30 UTC547
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC548
7ff8adafe9e4atagen2026-10-22 12:30 UTC549
    /// Login using a username and password.
7ff8adafe9e4atagen2026-10-22 12:30 UTC550
    pub async fn login(
7ff8adafe9e4atagen2026-10-22 12:30 UTC551
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC552
        username: String,
7ff8adafe9e4atagen2026-10-22 12:30 UTC553
        password: String,
7ff8adafe9e4atagen2026-10-22 12:30 UTC554
        initial_device_name: Option<String>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC555
        device_id: Option<String>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC556
    ) -> Result<(), ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC557
        let mut builder = self.inner.matrix_auth().login_username(&username, &password);
7ff8adafe9e4atagen2026-10-22 12:30 UTC558
7ff8adafe9e4atagen2026-10-22 12:30 UTC559
        if let Some(initial_device_name) = initial_device_name.as_ref() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC560
            builder = builder.initial_device_display_name(initial_device_name);
7ff8adafe9e4atagen2026-10-22 12:30 UTC561
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC562
7ff8adafe9e4atagen2026-10-22 12:30 UTC563
        if let Some(device_id) = device_id.as_ref() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC564
            builder = builder.device_id(device_id);
7ff8adafe9e4atagen2026-10-22 12:30 UTC565
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC566
7ff8adafe9e4atagen2026-10-22 12:30 UTC567
        builder.send().await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC568
7ff8adafe9e4atagen2026-10-22 12:30 UTC569
        Ok(())
7ff8adafe9e4atagen2026-10-22 12:30 UTC570
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC571
7ff8adafe9e4atagen2026-10-22 12:30 UTC572
    /// Login using JWT
7ff8adafe9e4atagen2026-10-22 12:30 UTC573
    /// This is an implementation of the custom_login https://docs.rs/matrix-sdk/latest/matrix_sdk/matrix_auth/struct.MatrixAuth.html#method.login_custom
7ff8adafe9e4atagen2026-10-22 12:30 UTC574
    /// For more information on logging in with JWT: https://element-hq.github.io/synapse/latest/jwt.html
7ff8adafe9e4atagen2026-10-22 12:30 UTC575
    pub async fn custom_login_with_jwt(
7ff8adafe9e4atagen2026-10-22 12:30 UTC576
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC577
        jwt: String,
7ff8adafe9e4atagen2026-10-22 12:30 UTC578
        initial_device_name: Option<String>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC579
        device_id: Option<String>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC580
    ) -> Result<(), ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC581
        let data = json!({ "token": jwt }).as_object().unwrap().clone();
7ff8adafe9e4atagen2026-10-22 12:30 UTC582
7ff8adafe9e4atagen2026-10-22 12:30 UTC583
        let mut builder = self.inner.matrix_auth().login_custom("org.matrix.login.jwt", data)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC584
7ff8adafe9e4atagen2026-10-22 12:30 UTC585
        if let Some(initial_device_name) = initial_device_name.as_ref() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC586
            builder = builder.initial_device_display_name(initial_device_name);
7ff8adafe9e4atagen2026-10-22 12:30 UTC587
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC588
7ff8adafe9e4atagen2026-10-22 12:30 UTC589
        if let Some(device_id) = device_id.as_ref() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC590
            builder = builder.device_id(device_id);
7ff8adafe9e4atagen2026-10-22 12:30 UTC591
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC592
7ff8adafe9e4atagen2026-10-22 12:30 UTC593
        builder.send().await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC594
7ff8adafe9e4atagen2026-10-22 12:30 UTC595
        Ok(())
7ff8adafe9e4atagen2026-10-22 12:30 UTC596
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC597
7ff8adafe9e4atagen2026-10-22 12:30 UTC598
    /// Login using an email and password.
7ff8adafe9e4atagen2026-10-22 12:30 UTC599
    pub async fn login_with_email(
7ff8adafe9e4atagen2026-10-22 12:30 UTC600
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC601
        email: String,
7ff8adafe9e4atagen2026-10-22 12:30 UTC602
        password: String,
7ff8adafe9e4atagen2026-10-22 12:30 UTC603
        initial_device_name: Option<String>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC604
        device_id: Option<String>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC605
    ) -> Result<(), ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC606
        let mut builder = self
7ff8adafe9e4atagen2026-10-22 12:30 UTC607
            .inner
7ff8adafe9e4atagen2026-10-22 12:30 UTC608
            .matrix_auth()
7ff8adafe9e4atagen2026-10-22 12:30 UTC609
            .login_identifier(UserIdentifier::Email(EmailUserIdentifier::new(email)), &password);
7ff8adafe9e4atagen2026-10-22 12:30 UTC610
7ff8adafe9e4atagen2026-10-22 12:30 UTC611
        if let Some(initial_device_name) = initial_device_name.as_ref() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC612
            builder = builder.initial_device_display_name(initial_device_name);
7ff8adafe9e4atagen2026-10-22 12:30 UTC613
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC614
7ff8adafe9e4atagen2026-10-22 12:30 UTC615
        if let Some(device_id) = device_id.as_ref() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC616
            builder = builder.device_id(device_id);
7ff8adafe9e4atagen2026-10-22 12:30 UTC617
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC618
7ff8adafe9e4atagen2026-10-22 12:30 UTC619
        builder.send().await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC620
7ff8adafe9e4atagen2026-10-22 12:30 UTC621
        Ok(())
7ff8adafe9e4atagen2026-10-22 12:30 UTC622
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC623
7ff8adafe9e4atagen2026-10-22 12:30 UTC624
    /// Returns a handler to start the SSO login process.
7ff8adafe9e4atagen2026-10-22 12:30 UTC625
    pub(crate) async fn start_sso_login(
7ff8adafe9e4atagen2026-10-22 12:30 UTC626
        self: &Arc<Self>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC627
        redirect_url: String,
7ff8adafe9e4atagen2026-10-22 12:30 UTC628
        idp_id: Option<String>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC629
    ) -> Result<Arc<SsoHandler>, SsoError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC630
        let auth = self.inner.matrix_auth();
7ff8adafe9e4atagen2026-10-22 12:30 UTC631
        let url = auth
7ff8adafe9e4atagen2026-10-22 12:30 UTC632
            .get_sso_login_url(redirect_url.as_str(), idp_id.as_deref())
7ff8adafe9e4atagen2026-10-22 12:30 UTC633
            .await
7ff8adafe9e4atagen2026-10-22 12:30 UTC634
            .map_err(|e| SsoError::Generic { message: e.to_string() })?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC635
        Ok(Arc::new(SsoHandler { client: Arc::clone(self), url }))
7ff8adafe9e4atagen2026-10-22 12:30 UTC636
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC637
7ff8adafe9e4atagen2026-10-22 12:30 UTC638
    /// Requests the URL needed for opening a web view using OAuth. Once the web
7ff8adafe9e4atagen2026-10-22 12:30 UTC639
    /// view has succeeded, call `login_with_oauth_callback` with the callback
7ff8adafe9e4atagen2026-10-22 12:30 UTC640
    /// it returns. If a failure occurs and a callback isn't available, make
7ff8adafe9e4atagen2026-10-22 12:30 UTC641
    /// sure to call `abort_oauth_auth` to inform the client of this.
7ff8adafe9e4atagen2026-10-22 12:30 UTC642
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC643
    /// # Arguments
7ff8adafe9e4atagen2026-10-22 12:30 UTC644
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC645
    /// * `oauth_configuration` - The configuration used to load the credentials
7ff8adafe9e4atagen2026-10-22 12:30 UTC646
    ///   of the client if it is already registered with the authorization
7ff8adafe9e4atagen2026-10-22 12:30 UTC647
    ///   server, or register the client and store its credentials if it isn't.
7ff8adafe9e4atagen2026-10-22 12:30 UTC648
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC649
    /// * `prompt` - The desired user experience in the web UI. No value means
7ff8adafe9e4atagen2026-10-22 12:30 UTC650
    ///   that the user wishes to login into an existing account, and a value of
7ff8adafe9e4atagen2026-10-22 12:30 UTC651
    ///   `Create` means that the user wishes to register a new account.
7ff8adafe9e4atagen2026-10-22 12:30 UTC652
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC653
    /// * `login_hint` - A generic login hint that an identity provider can use
7ff8adafe9e4atagen2026-10-22 12:30 UTC654
    ///   to pre-fill the login form. The format of this hint is not restricted
7ff8adafe9e4atagen2026-10-22 12:30 UTC655
    ///   by the spec as external providers all have their own way to handle the hint.
7ff8adafe9e4atagen2026-10-22 12:30 UTC656
    ///   However, it should be noted that when providing a user ID as a hint
7ff8adafe9e4atagen2026-10-22 12:30 UTC657
    ///   for MAS (with no upstream provider), then the format to use is defined
7ff8adafe9e4atagen2026-10-22 12:30 UTC658
    ///   by [MSC4198]: https://github.com/matrix-org/matrix-spec-proposals/pull/4198
7ff8adafe9e4atagen2026-10-22 12:30 UTC659
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC660
    /// * `device_id` - The unique ID that will be associated with the session.
7ff8adafe9e4atagen2026-10-22 12:30 UTC661
    ///   If not set, a random one will be generated. It can be an existing
7ff8adafe9e4atagen2026-10-22 12:30 UTC662
    ///   device ID from a previous login call. Note that this should be done
7ff8adafe9e4atagen2026-10-22 12:30 UTC663
    ///   only if the client also holds the corresponding encryption keys.
7ff8adafe9e4atagen2026-10-22 12:30 UTC664
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC665
    /// * `additional_scopes` - Additional scopes to request from the
7ff8adafe9e4atagen2026-10-22 12:30 UTC666
    ///   authorization server, e.g. "urn:matrix:client:com.example.msc9999.foo".
7ff8adafe9e4atagen2026-10-22 12:30 UTC667
    ///   The scopes for API access and the device ID according to the
7ff8adafe9e4atagen2026-10-22 12:30 UTC668
    ///   [specification](https://spec.matrix.org/v1.15/client-server-api/#allocated-scope-tokens)
7ff8adafe9e4atagen2026-10-22 12:30 UTC669
    ///   are always requested.
7ff8adafe9e4atagen2026-10-22 12:30 UTC670
    pub async fn url_for_oauth(
7ff8adafe9e4atagen2026-10-22 12:30 UTC671
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC672
        oauth_configuration: &OAuthConfiguration,
7ff8adafe9e4atagen2026-10-22 12:30 UTC673
        prompt: Option<OAuthPrompt>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC674
        login_hint: Option<String>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC675
        device_id: Option<String>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC676
        additional_scopes: Option<Vec<String>>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC677
    ) -> Result<Arc<OAuthAuthorizationData>, OAuthError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC678
        let registration_data = oauth_configuration.registration_data()?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC679
        let redirect_uri = oauth_configuration.redirect_uri()?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC680
7ff8adafe9e4atagen2026-10-22 12:30 UTC681
        let device_id = device_id.map(OwnedDeviceId::from);
7ff8adafe9e4atagen2026-10-22 12:30 UTC682
7ff8adafe9e4atagen2026-10-22 12:30 UTC683
        let additional_scopes =
7ff8adafe9e4atagen2026-10-22 12:30 UTC684
            additional_scopes.map(|scopes| scopes.into_iter().map(Scope::new).collect::<Vec<_>>());
7ff8adafe9e4atagen2026-10-22 12:30 UTC685
7ff8adafe9e4atagen2026-10-22 12:30 UTC686
        let mut url_builder = self.inner.oauth().login(
7ff8adafe9e4atagen2026-10-22 12:30 UTC687
            redirect_uri,
7ff8adafe9e4atagen2026-10-22 12:30 UTC688
            device_id,
7ff8adafe9e4atagen2026-10-22 12:30 UTC689
            Some(registration_data),
7ff8adafe9e4atagen2026-10-22 12:30 UTC690
            additional_scopes,
7ff8adafe9e4atagen2026-10-22 12:30 UTC691
        );
7ff8adafe9e4atagen2026-10-22 12:30 UTC692
7ff8adafe9e4atagen2026-10-22 12:30 UTC693
        if let Some(prompt) = prompt {
7ff8adafe9e4atagen2026-10-22 12:30 UTC694
            url_builder = url_builder.prompt(vec![prompt.into()]);
7ff8adafe9e4atagen2026-10-22 12:30 UTC695
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC696
        if let Some(login_hint) = login_hint {
7ff8adafe9e4atagen2026-10-22 12:30 UTC697
            url_builder = url_builder.login_hint(login_hint);
7ff8adafe9e4atagen2026-10-22 12:30 UTC698
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC699
7ff8adafe9e4atagen2026-10-22 12:30 UTC700
        let data = url_builder.build().await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC701
7ff8adafe9e4atagen2026-10-22 12:30 UTC702
        Ok(Arc::new(data))
7ff8adafe9e4atagen2026-10-22 12:30 UTC703
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC704
7ff8adafe9e4atagen2026-10-22 12:30 UTC705
    /// Aborts an existing OAuth login operation that might have been cancelled,
7ff8adafe9e4atagen2026-10-22 12:30 UTC706
    /// failed etc.
7ff8adafe9e4atagen2026-10-22 12:30 UTC707
    pub async fn abort_oauth_auth(&self, authorization_data: Arc<OAuthAuthorizationData>) {
7ff8adafe9e4atagen2026-10-22 12:30 UTC708
        self.inner.oauth().abort_login(&authorization_data.state).await;
7ff8adafe9e4atagen2026-10-22 12:30 UTC709
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC710
7ff8adafe9e4atagen2026-10-22 12:30 UTC711
    /// Completes the OAuth login process.
7ff8adafe9e4atagen2026-10-22 12:30 UTC712
    pub async fn login_with_oauth_callback(&self, callback_url: String) -> Result<(), OAuthError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC713
        let url = Url::parse(&callback_url).or(Err(OAuthError::CallbackUrlInvalid))?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC714
7ff8adafe9e4atagen2026-10-22 12:30 UTC715
        self.inner.oauth().finish_login(url.into()).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC716
7ff8adafe9e4atagen2026-10-22 12:30 UTC717
        Ok(())
7ff8adafe9e4atagen2026-10-22 12:30 UTC718
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC719
7ff8adafe9e4atagen2026-10-22 12:30 UTC720
    /// Create a handler for requesting an existing device to grant login to
7ff8adafe9e4atagen2026-10-22 12:30 UTC721
    /// this device by way of a QR code.
7ff8adafe9e4atagen2026-10-22 12:30 UTC722
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC723
    /// # Arguments
7ff8adafe9e4atagen2026-10-22 12:30 UTC724
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC725
    /// * `oauth_configuration` - The data to restore or register the client
7ff8adafe9e4atagen2026-10-22 12:30 UTC726
    ///   with the server.
7ff8adafe9e4atagen2026-10-22 12:30 UTC727
    pub fn new_login_with_qr_code_handler(
7ff8adafe9e4atagen2026-10-22 12:30 UTC728
        self: Arc<Self>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC729
        oauth_configuration: OAuthConfiguration,
7ff8adafe9e4atagen2026-10-22 12:30 UTC730
    ) -> LoginWithQrCodeHandler {
7ff8adafe9e4atagen2026-10-22 12:30 UTC731
        LoginWithQrCodeHandler::new(self.inner.oauth(), oauth_configuration)
7ff8adafe9e4atagen2026-10-22 12:30 UTC732
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC733
7ff8adafe9e4atagen2026-10-22 12:30 UTC734
    /// Create a handler for granting login from this device to a new device by
7ff8adafe9e4atagen2026-10-22 12:30 UTC735
    /// way of a QR code.
7ff8adafe9e4atagen2026-10-22 12:30 UTC736
    pub fn new_grant_login_with_qr_code_handler(self: Arc<Self>) -> GrantLoginWithQrCodeHandler {
7ff8adafe9e4atagen2026-10-22 12:30 UTC737
        GrantLoginWithQrCodeHandler::new(self.inner.oauth())
7ff8adafe9e4atagen2026-10-22 12:30 UTC738
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC739
7ff8adafe9e4atagen2026-10-22 12:30 UTC740
    /// Restores the client from a `Session`.
7ff8adafe9e4atagen2026-10-22 12:30 UTC741
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC742
    /// It reloads the entire set of rooms from the previous session.
7ff8adafe9e4atagen2026-10-22 12:30 UTC743
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC744
    /// If you want to control the amount of rooms to reloads, check
7ff8adafe9e4atagen2026-10-22 12:30 UTC745
    /// [`Client::restore_session_with`].
7ff8adafe9e4atagen2026-10-22 12:30 UTC746
    pub async fn restore_session(&self, session: Session) -> Result<(), ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC747
        self.restore_session_with(session, RoomLoadSettings::All).await
7ff8adafe9e4atagen2026-10-22 12:30 UTC748
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC749
7ff8adafe9e4atagen2026-10-22 12:30 UTC750
    /// Restores the client from a `Session`.
7ff8adafe9e4atagen2026-10-22 12:30 UTC751
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC752
    /// It reloads a set of rooms controlled by [`RoomLoadSettings`].
7ff8adafe9e4atagen2026-10-22 12:30 UTC753
    pub async fn restore_session_with(
7ff8adafe9e4atagen2026-10-22 12:30 UTC754
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC755
        session: Session,
7ff8adafe9e4atagen2026-10-22 12:30 UTC756
        room_load_settings: RoomLoadSettings,
7ff8adafe9e4atagen2026-10-22 12:30 UTC757
    ) -> Result<(), ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC758
        let sliding_sync_version = session.sliding_sync_version.clone();
7ff8adafe9e4atagen2026-10-22 12:30 UTC759
        let auth_session: AuthSession = session.try_into()?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC760
7ff8adafe9e4atagen2026-10-22 12:30 UTC761
        self.inner
7ff8adafe9e4atagen2026-10-22 12:30 UTC762
            .restore_session_with(
7ff8adafe9e4atagen2026-10-22 12:30 UTC763
                auth_session,
7ff8adafe9e4atagen2026-10-22 12:30 UTC764
                room_load_settings
7ff8adafe9e4atagen2026-10-22 12:30 UTC765
                    .try_into()
7ff8adafe9e4atagen2026-10-22 12:30 UTC766
                    .map_err(|error| ClientError::from_str(error, None))?,
7ff8adafe9e4atagen2026-10-22 12:30 UTC767
            )
7ff8adafe9e4atagen2026-10-22 12:30 UTC768
            .await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC769
        self.inner.set_sliding_sync_version(sliding_sync_version.try_into()?);
7ff8adafe9e4atagen2026-10-22 12:30 UTC770
7ff8adafe9e4atagen2026-10-22 12:30 UTC771
        Ok(())
7ff8adafe9e4atagen2026-10-22 12:30 UTC772
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC773
7ff8adafe9e4atagen2026-10-22 12:30 UTC774
    /// Enables or disables all the room send queues at once.
7ff8adafe9e4atagen2026-10-22 12:30 UTC775
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC776
    /// When connectivity is lost on a device, it is recommended to disable the
7ff8adafe9e4atagen2026-10-22 12:30 UTC777
    /// room sending queues.
7ff8adafe9e4atagen2026-10-22 12:30 UTC778
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC779
    /// This can be controlled for individual rooms, using
7ff8adafe9e4atagen2026-10-22 12:30 UTC780
    /// [`Room::enable_send_queue`].
7ff8adafe9e4atagen2026-10-22 12:30 UTC781
    pub async fn enable_all_send_queues(&self, enable: bool) {
7ff8adafe9e4atagen2026-10-22 12:30 UTC782
        self.inner.send_queue().set_enabled(enable).await;
7ff8adafe9e4atagen2026-10-22 12:30 UTC783
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC784
7ff8adafe9e4atagen2026-10-22 12:30 UTC785
    /// Enables or disables progress reporting for media uploads in the send
7ff8adafe9e4atagen2026-10-22 12:30 UTC786
    /// queue.
7ff8adafe9e4atagen2026-10-22 12:30 UTC787
    pub fn enable_send_queue_upload_progress(&self, enable: bool) {
7ff8adafe9e4atagen2026-10-22 12:30 UTC788
        self.inner.send_queue().enable_upload_progress(enable);
7ff8adafe9e4atagen2026-10-22 12:30 UTC789
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC790
7ff8adafe9e4atagen2026-10-22 12:30 UTC791
    /// Subscribe to the global send queue update reporter, at the
7ff8adafe9e4atagen2026-10-22 12:30 UTC792
    /// client-wide level.
7ff8adafe9e4atagen2026-10-22 12:30 UTC793
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC794
    /// The given listener will be immediately called with
7ff8adafe9e4atagen2026-10-22 12:30 UTC795
    /// `RoomSendQueueUpdate::NewLocalEvent` for each local echo existing in
7ff8adafe9e4atagen2026-10-22 12:30 UTC796
    /// the queue.
7ff8adafe9e4atagen2026-10-22 12:30 UTC797
    pub async fn subscribe_to_send_queue_updates(
7ff8adafe9e4atagen2026-10-22 12:30 UTC798
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC799
        listener: Box<dyn SendQueueRoomUpdateListener>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC800
    ) -> Result<Arc<TaskHandle>, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC801
        let q = self.inner.send_queue();
7ff8adafe9e4atagen2026-10-22 12:30 UTC802
        let local_echoes = q.local_echoes().await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC803
        let mut subscriber = q.subscribe();
7ff8adafe9e4atagen2026-10-22 12:30 UTC804
7ff8adafe9e4atagen2026-10-22 12:30 UTC805
        for (room_id, local_echoes) in local_echoes {
7ff8adafe9e4atagen2026-10-22 12:30 UTC806
            for local_echo in local_echoes {
7ff8adafe9e4atagen2026-10-22 12:30 UTC807
                listener.on_update(
7ff8adafe9e4atagen2026-10-22 12:30 UTC808
                    room_id.clone().into(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC809
                    RoomSendQueueUpdate::NewLocalEvent {
7ff8adafe9e4atagen2026-10-22 12:30 UTC810
                        transaction_id: local_echo.transaction_id.into(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC811
                    },
7ff8adafe9e4atagen2026-10-22 12:30 UTC812
                );
7ff8adafe9e4atagen2026-10-22 12:30 UTC813
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC814
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC815
7ff8adafe9e4atagen2026-10-22 12:30 UTC816
        Ok(Arc::new(TaskHandle::new(get_runtime_handle().spawn(async move {
7ff8adafe9e4atagen2026-10-22 12:30 UTC817
            loop {
7ff8adafe9e4atagen2026-10-22 12:30 UTC818
                match subscriber.recv().await {
7ff8adafe9e4atagen2026-10-22 12:30 UTC819
                    Ok(update) => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC820
                        let room_id = update.room_id.to_string();
7ff8adafe9e4atagen2026-10-22 12:30 UTC821
                        match update.update.try_into() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC822
                            Ok(update) => listener.on_update(room_id, update),
7ff8adafe9e4atagen2026-10-22 12:30 UTC823
                            Err(err) => error!("error when converting send queue update: {err}"),
7ff8adafe9e4atagen2026-10-22 12:30 UTC824
                        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC825
                    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC826
                    Err(err) => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC827
                        error!("error when listening to the send queue update reporter: {err}");
7ff8adafe9e4atagen2026-10-22 12:30 UTC828
                    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC829
                }
7ff8adafe9e4atagen2026-10-22 12:30 UTC830
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC831
        }))))
7ff8adafe9e4atagen2026-10-22 12:30 UTC832
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC833
7ff8adafe9e4atagen2026-10-22 12:30 UTC834
    /// Subscribe to the global enablement status of the send queue, at the
7ff8adafe9e4atagen2026-10-22 12:30 UTC835
    /// client-wide level.
7ff8adafe9e4atagen2026-10-22 12:30 UTC836
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC837
    /// The given listener will be immediately called with the initial value of
7ff8adafe9e4atagen2026-10-22 12:30 UTC838
    /// the enablement status.
7ff8adafe9e4atagen2026-10-22 12:30 UTC839
    pub fn subscribe_to_send_queue_status(
7ff8adafe9e4atagen2026-10-22 12:30 UTC840
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC841
        listener: Box<dyn SendQueueRoomErrorListener>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC842
    ) -> Arc<TaskHandle> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC843
        let q = self.inner.send_queue();
7ff8adafe9e4atagen2026-10-22 12:30 UTC844
        let mut subscriber = q.subscribe_errors();
7ff8adafe9e4atagen2026-10-22 12:30 UTC845
7ff8adafe9e4atagen2026-10-22 12:30 UTC846
        Arc::new(TaskHandle::new(get_runtime_handle().spawn(async move {
7ff8adafe9e4atagen2026-10-22 12:30 UTC847
            // Respawn tasks for rooms that had unsent events. At this point we've just
7ff8adafe9e4atagen2026-10-22 12:30 UTC848
            // created the subscriber, so it'll be notified about errors.
7ff8adafe9e4atagen2026-10-22 12:30 UTC849
            q.respawn_tasks_for_rooms_with_unsent_requests().await;
7ff8adafe9e4atagen2026-10-22 12:30 UTC850
7ff8adafe9e4atagen2026-10-22 12:30 UTC851
            loop {
7ff8adafe9e4atagen2026-10-22 12:30 UTC852
                match subscriber.recv().await {
7ff8adafe9e4atagen2026-10-22 12:30 UTC853
                    Ok(report) => listener
7ff8adafe9e4atagen2026-10-22 12:30 UTC854
                        .on_error(report.room_id.to_string(), ClientError::from_err(report.error)),
7ff8adafe9e4atagen2026-10-22 12:30 UTC855
                    Err(err) => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC856
                        error!("error when listening to the send queue error reporter: {err}");
7ff8adafe9e4atagen2026-10-22 12:30 UTC857
                    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC858
                }
7ff8adafe9e4atagen2026-10-22 12:30 UTC859
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC860
        })))
7ff8adafe9e4atagen2026-10-22 12:30 UTC861
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC862
7ff8adafe9e4atagen2026-10-22 12:30 UTC863
    /// Subscribe to duplicate key upload errors triggered by requests to
7ff8adafe9e4atagen2026-10-22 12:30 UTC864
    /// /keys/upload.
7ff8adafe9e4atagen2026-10-22 12:30 UTC865
    pub fn subscribe_to_duplicate_key_upload_errors(
7ff8adafe9e4atagen2026-10-22 12:30 UTC866
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC867
        listener: Box<dyn DuplicateKeyUploadErrorListener>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC868
    ) -> Arc<TaskHandle> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC869
        let mut subscriber = self.inner.subscribe_to_duplicate_key_upload_errors();
7ff8adafe9e4atagen2026-10-22 12:30 UTC870
7ff8adafe9e4atagen2026-10-22 12:30 UTC871
        Arc::new(TaskHandle::new(get_runtime_handle().spawn(async move {
7ff8adafe9e4atagen2026-10-22 12:30 UTC872
            loop {
7ff8adafe9e4atagen2026-10-22 12:30 UTC873
                match subscriber.recv().await {
7ff8adafe9e4atagen2026-10-22 12:30 UTC874
                    Ok(message) => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC875
                        listener.on_duplicate_key_upload_error(message.map(|m| m.into()))
7ff8adafe9e4atagen2026-10-22 12:30 UTC876
                    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC877
                    Err(err) => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC878
                        error!("error when listening to key upload errors: {err}");
7ff8adafe9e4atagen2026-10-22 12:30 UTC879
                    }
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
7ff8adafe9e4atagen2026-10-22 12:30 UTC885
    /// Subscribe to beacon_info updates for the current user across all rooms.
7ff8adafe9e4atagen2026-10-22 12:30 UTC886
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC887
    /// The listener is only called for new matching updates; there is no
7ff8adafe9e4atagen2026-10-22 12:30 UTC888
    /// initial replay.
7ff8adafe9e4atagen2026-10-22 12:30 UTC889
    pub fn subscribe_to_own_beacon_info_updates(
7ff8adafe9e4atagen2026-10-22 12:30 UTC890
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC891
        listener: Box<dyn BeaconInfoListener>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC892
    ) -> Result<Arc<TaskHandle>, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC893
        let stream = self.inner.observe_own_beacon_info_updates()?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC894
7ff8adafe9e4atagen2026-10-22 12:30 UTC895
        Ok(Arc::new(TaskHandle::new(get_runtime_handle().spawn(async move {
7ff8adafe9e4atagen2026-10-22 12:30 UTC896
            pin_mut!(stream);
7ff8adafe9e4atagen2026-10-22 12:30 UTC897
7ff8adafe9e4atagen2026-10-22 12:30 UTC898
            while let Some(update) = stream.next().await {
7ff8adafe9e4atagen2026-10-22 12:30 UTC899
                listener.on_update(update.into());
7ff8adafe9e4atagen2026-10-22 12:30 UTC900
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC901
        }))))
7ff8adafe9e4atagen2026-10-22 12:30 UTC902
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC903
7ff8adafe9e4atagen2026-10-22 12:30 UTC904
    /// Subscribe to updates of global account data events.
7ff8adafe9e4atagen2026-10-22 12:30 UTC905
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC906
    /// Be careful that only the most recent value can be observed. Subscribers
7ff8adafe9e4atagen2026-10-22 12:30 UTC907
    /// are notified when a new value is sent, but there is no guarantee that
7ff8adafe9e4atagen2026-10-22 12:30 UTC908
    /// they will see all values.
7ff8adafe9e4atagen2026-10-22 12:30 UTC909
    pub fn observe_account_data_event(
7ff8adafe9e4atagen2026-10-22 12:30 UTC910
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC911
        event_type: AccountDataEventType,
7ff8adafe9e4atagen2026-10-22 12:30 UTC912
        listener: Box<dyn AccountDataListener>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC913
    ) -> Arc<TaskHandle> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC914
        macro_rules! observe {
7ff8adafe9e4atagen2026-10-22 12:30 UTC915
            ($t:ty, $cb: expr) => {{
7ff8adafe9e4atagen2026-10-22 12:30 UTC916
                // Using an Arc here is mandatory or else the subscriber will never trigger
7ff8adafe9e4atagen2026-10-22 12:30 UTC917
                let observer =
7ff8adafe9e4atagen2026-10-22 12:30 UTC918
                    Arc::new(self.inner.observe_events::<RumaGlobalAccountDataEvent<$t>, ()>());
7ff8adafe9e4atagen2026-10-22 12:30 UTC919
7ff8adafe9e4atagen2026-10-22 12:30 UTC920
                Arc::new(TaskHandle::new(get_runtime_handle().spawn(async move {
7ff8adafe9e4atagen2026-10-22 12:30 UTC921
                    let mut subscriber = observer.subscribe();
7ff8adafe9e4atagen2026-10-22 12:30 UTC922
                    loop {
7ff8adafe9e4atagen2026-10-22 12:30 UTC923
                        if let Some(next) = subscriber.next().await {
7ff8adafe9e4atagen2026-10-22 12:30 UTC924
                            $cb(next.0);
7ff8adafe9e4atagen2026-10-22 12:30 UTC925
                        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC926
                    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC927
                })))
7ff8adafe9e4atagen2026-10-22 12:30 UTC928
            }};
7ff8adafe9e4atagen2026-10-22 12:30 UTC929
7ff8adafe9e4atagen2026-10-22 12:30 UTC930
            ($t:ty) => {{
7ff8adafe9e4atagen2026-10-22 12:30 UTC931
                observe!($t, |event: RumaGlobalAccountDataEvent<$t>| {
7ff8adafe9e4atagen2026-10-22 12:30 UTC932
                    listener.on_change(event.into());
7ff8adafe9e4atagen2026-10-22 12:30 UTC933
                })
7ff8adafe9e4atagen2026-10-22 12:30 UTC934
            }};
7ff8adafe9e4atagen2026-10-22 12:30 UTC935
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC936
7ff8adafe9e4atagen2026-10-22 12:30 UTC937
        match event_type {
7ff8adafe9e4atagen2026-10-22 12:30 UTC938
            AccountDataEventType::Direct => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC939
                observe!(DirectEventContent)
7ff8adafe9e4atagen2026-10-22 12:30 UTC940
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC941
            AccountDataEventType::IdentityServer => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC942
                observe!(IdentityServerEventContent)
7ff8adafe9e4atagen2026-10-22 12:30 UTC943
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC944
            AccountDataEventType::IgnoredUserList => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC945
                observe!(IgnoredUserListEventContent)
7ff8adafe9e4atagen2026-10-22 12:30 UTC946
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC947
            AccountDataEventType::PushRules => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC948
                observe!(PushRulesEventContent, |event: RumaGlobalAccountDataEvent<
7ff8adafe9e4atagen2026-10-22 12:30 UTC949
                    PushRulesEventContent,
7ff8adafe9e4atagen2026-10-22 12:30 UTC950
                >| {
7ff8adafe9e4atagen2026-10-22 12:30 UTC951
                    if let Ok(event) = event.try_into() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC952
                        listener.on_change(event);
7ff8adafe9e4atagen2026-10-22 12:30 UTC953
                    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC954
                })
7ff8adafe9e4atagen2026-10-22 12:30 UTC955
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC956
            AccountDataEventType::SecretStorageDefaultKey => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC957
                observe!(SecretStorageDefaultKeyEventContent)
7ff8adafe9e4atagen2026-10-22 12:30 UTC958
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC959
            AccountDataEventType::SecretStorageKey { key_id } => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC960
                observe!(SecretStorageKeyEventContent, |event: RumaGlobalAccountDataEvent<
7ff8adafe9e4atagen2026-10-22 12:30 UTC961
                    SecretStorageKeyEventContent,
7ff8adafe9e4atagen2026-10-22 12:30 UTC962
                >| {
7ff8adafe9e4atagen2026-10-22 12:30 UTC963
                    if event.content.key_id != key_id {
7ff8adafe9e4atagen2026-10-22 12:30 UTC964
                        return;
7ff8adafe9e4atagen2026-10-22 12:30 UTC965
                    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC966
                    if let Ok(event) = event.try_into() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC967
                        listener.on_change(event);
7ff8adafe9e4atagen2026-10-22 12:30 UTC968
                    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC969
                })
7ff8adafe9e4atagen2026-10-22 12:30 UTC970
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC971
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC972
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC973
7ff8adafe9e4atagen2026-10-22 12:30 UTC974
    /// Subscribe to updates of room account data events.
7ff8adafe9e4atagen2026-10-22 12:30 UTC975
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC976
    /// Be careful that only the most recent value can be observed. Subscribers
7ff8adafe9e4atagen2026-10-22 12:30 UTC977
    /// are notified when a new value is sent, but there is no guarantee that
7ff8adafe9e4atagen2026-10-22 12:30 UTC978
    /// they will see all values.
7ff8adafe9e4atagen2026-10-22 12:30 UTC979
    pub fn observe_room_account_data_event(
7ff8adafe9e4atagen2026-10-22 12:30 UTC980
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC981
        room_id: String,
7ff8adafe9e4atagen2026-10-22 12:30 UTC982
        event_type: RoomAccountDataEventType,
7ff8adafe9e4atagen2026-10-22 12:30 UTC983
        listener: Box<dyn RoomAccountDataListener>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC984
    ) -> Result<Arc<TaskHandle>, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC985
        macro_rules! observe {
7ff8adafe9e4atagen2026-10-22 12:30 UTC986
            ($t:ty, $cb: expr) => {{
7ff8adafe9e4atagen2026-10-22 12:30 UTC987
                // Using an Arc here is mandatory or else the subscriber will never trigger
7ff8adafe9e4atagen2026-10-22 12:30 UTC988
                let observer =
7ff8adafe9e4atagen2026-10-22 12:30 UTC989
                    Arc::new(self.inner.observe_room_events::<RumaRoomAccountDataEvent<$t>, ()>(
7ff8adafe9e4atagen2026-10-22 12:30 UTC990
                        &RoomId::parse(&room_id)?,
7ff8adafe9e4atagen2026-10-22 12:30 UTC991
                    ));
7ff8adafe9e4atagen2026-10-22 12:30 UTC992
7ff8adafe9e4atagen2026-10-22 12:30 UTC993
                Ok(Arc::new(TaskHandle::new(get_runtime_handle().spawn(async move {
7ff8adafe9e4atagen2026-10-22 12:30 UTC994
                    let mut subscriber = observer.subscribe();
7ff8adafe9e4atagen2026-10-22 12:30 UTC995
                    loop {
7ff8adafe9e4atagen2026-10-22 12:30 UTC996
                        if let Some(next) = subscriber.next().await {
7ff8adafe9e4atagen2026-10-22 12:30 UTC997
                            $cb(next.0);
7ff8adafe9e4atagen2026-10-22 12:30 UTC998
                        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC999
                    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1000
                }))))
7ff8adafe9e4atagen2026-10-22 12:30 UTC1001
            }};
7ff8adafe9e4atagen2026-10-22 12:30 UTC1002
7ff8adafe9e4atagen2026-10-22 12:30 UTC1003
            ($t:ty) => {{
7ff8adafe9e4atagen2026-10-22 12:30 UTC1004
                observe!($t, |event: RumaRoomAccountDataEvent<$t>| {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1005
                    listener.on_change(event.into(), room_id.clone());
7ff8adafe9e4atagen2026-10-22 12:30 UTC1006
                })
7ff8adafe9e4atagen2026-10-22 12:30 UTC1007
            }};
7ff8adafe9e4atagen2026-10-22 12:30 UTC1008
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1009
7ff8adafe9e4atagen2026-10-22 12:30 UTC1010
        match event_type {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1011
            RoomAccountDataEventType::FullyRead => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1012
                observe!(FullyReadEventContent)
7ff8adafe9e4atagen2026-10-22 12:30 UTC1013
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1014
            RoomAccountDataEventType::MarkedUnread => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1015
                observe!(MarkedUnreadEventContent)
7ff8adafe9e4atagen2026-10-22 12:30 UTC1016
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1017
            RoomAccountDataEventType::Tag => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1018
                observe!(TagEventContent, |event: RumaRoomAccountDataEvent<TagEventContent>| {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1019
                    if let Ok(event) = event.try_into() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1020
                        listener.on_change(event, room_id.clone());
7ff8adafe9e4atagen2026-10-22 12:30 UTC1021
                    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1022
                })
7ff8adafe9e4atagen2026-10-22 12:30 UTC1023
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1024
            RoomAccountDataEventType::UnstableMarkedUnread => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1025
                observe!(UnstableMarkedUnreadEventContent)
7ff8adafe9e4atagen2026-10-22 12:30 UTC1026
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1027
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1028
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1029
7ff8adafe9e4atagen2026-10-22 12:30 UTC1030
    /// Register a handler for notifications generated from sync responses.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1031
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC1032
    /// The handler will be called during sync for each event that triggers
7ff8adafe9e4atagen2026-10-22 12:30 UTC1033
    /// a notification based on the user's push rules.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1034
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC1035
    /// The handler receives:
7ff8adafe9e4atagen2026-10-22 12:30 UTC1036
    /// - The notification with push actions and event data
7ff8adafe9e4atagen2026-10-22 12:30 UTC1037
    /// - The room ID where the notification occurred
7ff8adafe9e4atagen2026-10-22 12:30 UTC1038
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC1039
    /// This is useful for implementing custom notification logic, such as
7ff8adafe9e4atagen2026-10-22 12:30 UTC1040
    /// displaying local notifications or updating notification badges.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1041
    pub async fn register_notification_handler(&self, listener: Box<dyn SyncNotificationListener>) {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1042
        let listener = Arc::new(listener);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1043
        self.inner
7ff8adafe9e4atagen2026-10-22 12:30 UTC1044
            .register_notification_handler(move |notification, room, _client| {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1045
                notification_handler(notification, room, listener.clone())
7ff8adafe9e4atagen2026-10-22 12:30 UTC1046
            })
7ff8adafe9e4atagen2026-10-22 12:30 UTC1047
            .await;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1048
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1049
7ff8adafe9e4atagen2026-10-22 12:30 UTC1050
    /// Allows generic GET requests to be made through the SDK's internal HTTP
7ff8adafe9e4atagen2026-10-22 12:30 UTC1051
    /// client. This is useful when the caller's native HTTP client wouldn't
7ff8adafe9e4atagen2026-10-22 12:30 UTC1052
    /// have the same configuration (such as certificates, proxies, etc.) This
7ff8adafe9e4atagen2026-10-22 12:30 UTC1053
    /// method returns the raw bytes of the response, so that any kind of
7ff8adafe9e4atagen2026-10-22 12:30 UTC1054
    /// resource can be fetched including images, files, etc.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1055
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC1056
    /// Note: When an HTTP error occurs, the error response can be found in the
7ff8adafe9e4atagen2026-10-22 12:30 UTC1057
    /// `ClientError::Generic`'s `details` field.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1058
    pub async fn get_url(&self, url: String) -> Result<Vec<u8>, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1059
        let response = self.inner.http_client().get(url).send().await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1060
        if response.status().is_success() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1061
            Ok(response.bytes().await?.into())
7ff8adafe9e4atagen2026-10-22 12:30 UTC1062
        } else {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1063
            Err(ClientError::Generic {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1064
                msg: response.status().to_string(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1065
                details: response.text().await.ok(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1066
            })
7ff8adafe9e4atagen2026-10-22 12:30 UTC1067
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1068
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1069
7ff8adafe9e4atagen2026-10-22 12:30 UTC1070
    /// Empty the server version and unstable features cache.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1071
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC1072
    /// Since the SDK caches the supported versions, it's possible to have a
7ff8adafe9e4atagen2026-10-22 12:30 UTC1073
    /// stale entry in the cache. This functions makes it possible to force
7ff8adafe9e4atagen2026-10-22 12:30 UTC1074
    /// reset it.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1075
    pub async fn reset_supported_versions(&self) -> Result<(), ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1076
        Ok(self.inner.reset_supported_versions().await?)
7ff8adafe9e4atagen2026-10-22 12:30 UTC1077
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1078
7ff8adafe9e4atagen2026-10-22 12:30 UTC1079
    /// Empty the well-known cache.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1080
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC1081
    /// Since the SDK caches the well-known, it's possible to have a stale
7ff8adafe9e4atagen2026-10-22 12:30 UTC1082
    /// entry in the cache. This functions makes it possible to force reset
7ff8adafe9e4atagen2026-10-22 12:30 UTC1083
    /// it.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1084
    pub async fn reset_well_known(&self) -> Result<(), ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1085
        Ok(self.inner.reset_well_known().await?)
7ff8adafe9e4atagen2026-10-22 12:30 UTC1086
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1087
7ff8adafe9e4atagen2026-10-22 12:30 UTC1088
    /// Retrieves a media file from the media source
7ff8adafe9e4atagen2026-10-22 12:30 UTC1089
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC1090
    /// Not available on Wasm platforms, due to lack of accessible file system.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1091
    pub async fn get_media_file(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1092
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1093
        media_source: Arc<MediaSource>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1094
        filename: Option<String>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1095
        mime_type: String,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1096
        use_cache: bool,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1097
        temp_dir: Option<String>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1098
    ) -> Result<Arc<MediaFileHandle>, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1099
        #[cfg(not(target_family = "wasm"))]
7ff8adafe9e4atagen2026-10-22 12:30 UTC1100
        {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1101
            let source = (*media_source).clone();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1102
            let mime_type: mime::Mime = mime_type.parse()?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1103
7ff8adafe9e4atagen2026-10-22 12:30 UTC1104
            let handle = self
7ff8adafe9e4atagen2026-10-22 12:30 UTC1105
                .inner
7ff8adafe9e4atagen2026-10-22 12:30 UTC1106
                .media()
7ff8adafe9e4atagen2026-10-22 12:30 UTC1107
                .get_media_file(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1108
                    &MediaRequestParameters {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1109
                        source: source.media_source,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1110
                        format: MediaFormat::File,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1111
                    },
7ff8adafe9e4atagen2026-10-22 12:30 UTC1112
                    filename,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1113
                    &mime_type,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1114
                    use_cache,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1115
                    temp_dir,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1116
                )
7ff8adafe9e4atagen2026-10-22 12:30 UTC1117
                .await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1118
7ff8adafe9e4atagen2026-10-22 12:30 UTC1119
            Ok(Arc::new(MediaFileHandle::new(handle)))
7ff8adafe9e4atagen2026-10-22 12:30 UTC1120
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1121
7ff8adafe9e4atagen2026-10-22 12:30 UTC1122
        /// MediaFileHandle uses SdkMediaFileHandle which requires an
7ff8adafe9e4atagen2026-10-22 12:30 UTC1123
        /// intermediate TempFile which is not available on wasm
7ff8adafe9e4atagen2026-10-22 12:30 UTC1124
        /// platforms due to lack of an accessible file system.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1125
        #[cfg(target_family = "wasm")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC1126
        Err(ClientError::Generic {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1127
            msg: "get_media_file is not supported on wasm platforms".to_owned(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1128
            details: None,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1129
        })
7ff8adafe9e4atagen2026-10-22 12:30 UTC1130
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1131
7ff8adafe9e4atagen2026-10-22 12:30 UTC1132
    pub async fn set_display_name(&self, name: String) -> Result<(), ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1133
        #[cfg(not(target_family = "wasm"))]
7ff8adafe9e4atagen2026-10-22 12:30 UTC1134
        {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1135
            self.inner
7ff8adafe9e4atagen2026-10-22 12:30 UTC1136
                .account()
7ff8adafe9e4atagen2026-10-22 12:30 UTC1137
                .set_display_name(Some(name.as_str()))
7ff8adafe9e4atagen2026-10-22 12:30 UTC1138
                .await
7ff8adafe9e4atagen2026-10-22 12:30 UTC1139
                .context("Unable to set display name")?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1140
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1141
7ff8adafe9e4atagen2026-10-22 12:30 UTC1142
        #[cfg(target_family = "wasm")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC1143
        {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1144
            self.inner.account().set_display_name(Some(name.as_str())).await.map_err(|e| {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1145
                ClientError::Generic {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1146
                    msg: "Unable to set display name".to_owned(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1147
                    details: Some(e.to_string()),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1148
                }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1149
            })?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1150
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1151
7ff8adafe9e4atagen2026-10-22 12:30 UTC1152
        Ok(())
7ff8adafe9e4atagen2026-10-22 12:30 UTC1153
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1154
7ff8adafe9e4atagen2026-10-22 12:30 UTC1155
    /// The sliding sync version.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1156
    pub fn sliding_sync_version(&self) -> SlidingSyncVersion {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1157
        self.inner.sliding_sync_version().into()
7ff8adafe9e4atagen2026-10-22 12:30 UTC1158
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1159
7ff8adafe9e4atagen2026-10-22 12:30 UTC1160
    /// Find all sliding sync versions that are available.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1161
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC1162
    /// Be careful: This method may hit the store and will send new requests for
7ff8adafe9e4atagen2026-10-22 12:30 UTC1163
    /// each call. It can be costly to call it repeatedly.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1164
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC1165
    /// If `.well-known` or `/versions` is unreachable, it will simply move
7ff8adafe9e4atagen2026-10-22 12:30 UTC1166
    /// potential sliding sync versions aside. No error will be reported.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1167
    pub async fn available_sliding_sync_versions(&self) -> Vec<SlidingSyncVersion> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1168
        self.inner.available_sliding_sync_versions().await.into_iter().map(Into::into).collect()
7ff8adafe9e4atagen2026-10-22 12:30 UTC1169
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1170
7ff8adafe9e4atagen2026-10-22 12:30 UTC1171
    /// Sets the [ClientDelegate] which will inform about authentication errors.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1172
    /// Returns an error if the delegate was already set.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1173
    pub fn set_delegate(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1174
        self: Arc<Self>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1175
        delegate: Option<Box<dyn ClientDelegate>>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1176
    ) -> Result<Option<Arc<TaskHandle>>, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1177
        if self.delegate_data.get().is_some() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1178
            return Err(ClientError::Generic {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1179
                msg: "Delegate already initialized".to_owned(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1180
                details: None,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1181
            });
7ff8adafe9e4atagen2026-10-22 12:30 UTC1182
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1183
7ff8adafe9e4atagen2026-10-22 12:30 UTC1184
        let handle = delegate.map(|delegate| {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1185
            let mut session_change_receiver = self.inner.subscribe_to_session_changes();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1186
            let client_clone = self.clone();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1187
            let session_change_task = get_runtime_handle().spawn(async move {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1188
                loop {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1189
                    match session_change_receiver.recv().await {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1190
                        Ok(session_change) => client_clone.process_session_change(session_change),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1191
                        Err(receive_error) => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1192
                            if let RecvError::Closed = receive_error {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1193
                                break;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1194
                            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1195
                        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1196
                    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1197
                }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1198
            });
7ff8adafe9e4atagen2026-10-22 12:30 UTC1199
7ff8adafe9e4atagen2026-10-22 12:30 UTC1200
            let delegate: Arc<dyn ClientDelegate> = delegate.into();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1201
7ff8adafe9e4atagen2026-10-22 12:30 UTC1202
            let client = self.inner.clone();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1203
            let delegate_clone = delegate.clone();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1204
            let task = Arc::new(AbortOnDrop::new(get_runtime_handle().spawn(async move {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1205
                let mut receiver = client.task_monitor().subscribe();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1206
                while let Ok(error) = receiver.recv().await {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1207
                    delegate_clone.on_background_task_error_report(error.task.name, error.reason);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1208
                }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1209
            })));
7ff8adafe9e4atagen2026-10-22 12:30 UTC1210
7ff8adafe9e4atagen2026-10-22 12:30 UTC1211
            let delegate_data =
7ff8adafe9e4atagen2026-10-22 12:30 UTC1212
                ClientDelegateData { delegate, _background_error_listener_task: task };
7ff8adafe9e4atagen2026-10-22 12:30 UTC1213
7ff8adafe9e4atagen2026-10-22 12:30 UTC1214
            self.delegate_data.get_or_init(|| delegate_data);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1215
7ff8adafe9e4atagen2026-10-22 12:30 UTC1216
            Arc::new(TaskHandle::new(session_change_task))
7ff8adafe9e4atagen2026-10-22 12:30 UTC1217
        });
7ff8adafe9e4atagen2026-10-22 12:30 UTC1218
7ff8adafe9e4atagen2026-10-22 12:30 UTC1219
        Ok(handle)
7ff8adafe9e4atagen2026-10-22 12:30 UTC1220
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1221
7ff8adafe9e4atagen2026-10-22 12:30 UTC1222
    /// Sets the [`UnableToDecryptDelegate`] which will inform about UTDs.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1223
    /// Returns an error if the delegate was already set.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1224
    pub async fn set_utd_delegate(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1225
        self: Arc<Self>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1226
        utd_delegate: Box<dyn UnableToDecryptDelegate>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1227
    ) -> Result<(), ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1228
        if self.utd_hook_manager.get().is_some() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1229
            return Err(ClientError::Generic {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1230
                msg: "UTD delegate already initialized".to_owned(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1231
                details: None,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1232
            });
7ff8adafe9e4atagen2026-10-22 12:30 UTC1233
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1234
7ff8adafe9e4atagen2026-10-22 12:30 UTC1235
        // UTDs detected before this duration may be reclassified as "late decryption"
7ff8adafe9e4atagen2026-10-22 12:30 UTC1236
        // events (or discarded, if they get decrypted fast enough).
7ff8adafe9e4atagen2026-10-22 12:30 UTC1237
        const UTD_HOOK_GRACE_PERIOD: Duration = Duration::from_secs(60);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1238
7ff8adafe9e4atagen2026-10-22 12:30 UTC1239
        let mut utd_hook_manager = UtdHookManager::new(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1240
            Arc::new(UtdHook { delegate: utd_delegate.into() }),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1241
            (*self.inner).clone(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1242
        )
7ff8adafe9e4atagen2026-10-22 12:30 UTC1243
        .with_max_delay(UTD_HOOK_GRACE_PERIOD);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1244
7ff8adafe9e4atagen2026-10-22 12:30 UTC1245
        if let Err(e) = utd_hook_manager.reload_from_store().await {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1246
            error!("Unable to reload UTD hook data from data store: {e}");
7ff8adafe9e4atagen2026-10-22 12:30 UTC1247
            // Carry on with the setup anyway; we shouldn't fail setup just
7ff8adafe9e4atagen2026-10-22 12:30 UTC1248
            // because the UTD hook failed to load its data.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1249
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1250
7ff8adafe9e4atagen2026-10-22 12:30 UTC1251
        self.utd_hook_manager.get_or_init(|| Arc::new(utd_hook_manager));
7ff8adafe9e4atagen2026-10-22 12:30 UTC1252
7ff8adafe9e4atagen2026-10-22 12:30 UTC1253
        Ok(())
7ff8adafe9e4atagen2026-10-22 12:30 UTC1254
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1255
7ff8adafe9e4atagen2026-10-22 12:30 UTC1256
    pub fn session(&self) -> Result<Session, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1257
        Self::session_inner((*self.inner).clone())
7ff8adafe9e4atagen2026-10-22 12:30 UTC1258
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1259
7ff8adafe9e4atagen2026-10-22 12:30 UTC1260
    pub async fn account_url(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1261
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1262
        action: Option<AccountManagementAction>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1263
    ) -> Result<Option<String>, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1264
        if !matches!(self.inner.auth_api(), Some(AuthApi::OAuth(..))) {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1265
            return Ok(None);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1266
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1267
7ff8adafe9e4atagen2026-10-22 12:30 UTC1268
        let server_metadata = match self.inner.oauth().cached_server_metadata().await {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1269
            Ok(server_metadata) => server_metadata,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1270
            Err(e) => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1271
                error!("Failed retrieving cached server metadata: {e}");
7ff8adafe9e4atagen2026-10-22 12:30 UTC1272
                return Err(SdkOAuthError::from(e).into());
7ff8adafe9e4atagen2026-10-22 12:30 UTC1273
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1274
        };
7ff8adafe9e4atagen2026-10-22 12:30 UTC1275
7ff8adafe9e4atagen2026-10-22 12:30 UTC1276
        Ok(if let Some(action) = &action {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1277
            server_metadata.account_management_url_with_action(action.into())
7ff8adafe9e4atagen2026-10-22 12:30 UTC1278
        } else {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1279
            server_metadata.account_management_uri
7ff8adafe9e4atagen2026-10-22 12:30 UTC1280
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1281
        .map(Into::into))
7ff8adafe9e4atagen2026-10-22 12:30 UTC1282
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1283
7ff8adafe9e4atagen2026-10-22 12:30 UTC1284
    pub fn user_id(&self) -> Result<String, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1285
        let user_id = self.inner.user_id().context("No User ID found")?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1286
        Ok(user_id.to_string())
7ff8adafe9e4atagen2026-10-22 12:30 UTC1287
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1288
7ff8adafe9e4atagen2026-10-22 12:30 UTC1289
    /// The server name part of the current user ID
7ff8adafe9e4atagen2026-10-22 12:30 UTC1290
    pub fn user_id_server_name(&self) -> Result<String, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1291
        let user_id = self.inner.user_id().context("No User ID found")?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1292
        Ok(user_id.server_name().to_string())
7ff8adafe9e4atagen2026-10-22 12:30 UTC1293
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1294
7ff8adafe9e4atagen2026-10-22 12:30 UTC1295
    pub async fn display_name(&self) -> Result<String, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1296
        let display_name =
7ff8adafe9e4atagen2026-10-22 12:30 UTC1297
            self.inner.account().get_display_name().await?.context("No User ID found")?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1298
        Ok(display_name)
7ff8adafe9e4atagen2026-10-22 12:30 UTC1299
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1300
7ff8adafe9e4atagen2026-10-22 12:30 UTC1301
    pub async fn request_openid_token(&self) -> Result<OpenIdToken, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1302
        Ok(self.inner.account().request_openid_token().await?.into())
7ff8adafe9e4atagen2026-10-22 12:30 UTC1303
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1304
7ff8adafe9e4atagen2026-10-22 12:30 UTC1305
    pub async fn upload_avatar(&self, mime_type: String, data: Vec<u8>) -> Result<(), ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1306
        let mime: Mime = mime_type.parse()?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1307
        self.inner.account().upload_avatar(&mime, data).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1308
        Ok(())
7ff8adafe9e4atagen2026-10-22 12:30 UTC1309
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1310
7ff8adafe9e4atagen2026-10-22 12:30 UTC1311
    /// Updates the user's avatar using the provided MXC url.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1312
    pub async fn set_avatar_url(&self, url: String) -> Result<(), ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1313
        // MxcUri can't just be instantiated, serde deserialization seems to be the only
7ff8adafe9e4atagen2026-10-22 12:30 UTC1314
        // way
7ff8adafe9e4atagen2026-10-22 12:30 UTC1315
        let mxc = serde_json::from_str::<OwnedMxcUri>(&url)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1316
        // Validate the newly generated MxcUri
7ff8adafe9e4atagen2026-10-22 12:30 UTC1317
        mxc.validate().map_err(ClientError::from_err)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1318
7ff8adafe9e4atagen2026-10-22 12:30 UTC1319
        self.inner.account().set_avatar_url(Some(&mxc)).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1320
        Ok(())
7ff8adafe9e4atagen2026-10-22 12:30 UTC1321
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1322
7ff8adafe9e4atagen2026-10-22 12:30 UTC1323
    pub async fn remove_avatar(&self) -> Result<(), ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1324
        self.inner.account().set_avatar_url(None).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1325
        Ok(())
7ff8adafe9e4atagen2026-10-22 12:30 UTC1326
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1327
7ff8adafe9e4atagen2026-10-22 12:30 UTC1328
    /// Sends a request to retrieve the avatar URL. Will fill the cache used by
7ff8adafe9e4atagen2026-10-22 12:30 UTC1329
    /// [`Self::cached_avatar_url`] on success.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1330
    pub async fn avatar_url(&self) -> Result<Option<String>, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1331
        let avatar_url = self.inner.account().get_avatar_url().await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1332
7ff8adafe9e4atagen2026-10-22 12:30 UTC1333
        Ok(avatar_url.map(|u| u.to_string()))
7ff8adafe9e4atagen2026-10-22 12:30 UTC1334
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1335
7ff8adafe9e4atagen2026-10-22 12:30 UTC1336
    /// Retrieves an avatar cached from a previous call to [`Self::avatar_url`].
7ff8adafe9e4atagen2026-10-22 12:30 UTC1337
    pub async fn cached_avatar_url(&self) -> Result<Option<String>, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1338
        Ok(self.inner.account().get_cached_avatar_url().await?.map(Into::into))
7ff8adafe9e4atagen2026-10-22 12:30 UTC1339
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1340
7ff8adafe9e4atagen2026-10-22 12:30 UTC1341
    pub fn device_id(&self) -> Result<String, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1342
        let device_id = self.inner.device_id().context("No Device ID found")?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1343
        Ok(device_id.to_string())
7ff8adafe9e4atagen2026-10-22 12:30 UTC1344
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1345
7ff8adafe9e4atagen2026-10-22 12:30 UTC1346
    pub async fn create_room(&self, request: CreateRoomParameters) -> Result<String, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1347
        let response = self.inner.create_room(request.try_into()?).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1348
        Ok(String::from(response.room_id()))
7ff8adafe9e4atagen2026-10-22 12:30 UTC1349
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1350
7ff8adafe9e4atagen2026-10-22 12:30 UTC1351
    /// Get the content of the event of the given type out of the account data
7ff8adafe9e4atagen2026-10-22 12:30 UTC1352
    /// store.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1353
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC1354
    /// It will be returned as a JSON string.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1355
    pub async fn account_data(&self, event_type: String) -> Result<Option<String>, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1356
        let event = self.inner.account().account_data_raw(event_type.into()).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1357
        Ok(event.map(|e| e.json().get().to_owned()))
7ff8adafe9e4atagen2026-10-22 12:30 UTC1358
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1359
7ff8adafe9e4atagen2026-10-22 12:30 UTC1360
    /// Set the given account data content for the given event type.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1361
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC1362
    /// It should be supplied as a JSON string.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1363
    pub async fn set_account_data(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1364
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1365
        event_type: String,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1366
        content: String,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1367
    ) -> Result<(), ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1368
        let raw_content = Raw::from_json_string(content)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1369
        self.inner.account().set_account_data_raw(event_type.into(), raw_content).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1370
        Ok(())
7ff8adafe9e4atagen2026-10-22 12:30 UTC1371
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1372
7ff8adafe9e4atagen2026-10-22 12:30 UTC1373
    pub async fn upload_media(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1374
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1375
        mime_type: String,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1376
        data: Vec<u8>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1377
        progress_watcher: Option<Box<dyn ProgressWatcher>>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1378
    ) -> Result<String, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1379
        let mime_type: mime::Mime = mime_type.parse().context("Parsing mime type")?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1380
        let request = self.inner.media().upload(&mime_type, data, None);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1381
7ff8adafe9e4atagen2026-10-22 12:30 UTC1382
        if let Some(progress_watcher) = progress_watcher {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1383
            let mut subscriber = request.subscribe_to_send_progress();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1384
            get_runtime_handle().spawn(async move {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1385
                while let Some(progress) = subscriber.next().await {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1386
                    progress_watcher.transmission_progress(progress.into());
7ff8adafe9e4atagen2026-10-22 12:30 UTC1387
                }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1388
            });
7ff8adafe9e4atagen2026-10-22 12:30 UTC1389
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1390
7ff8adafe9e4atagen2026-10-22 12:30 UTC1391
        let response = request.await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1392
7ff8adafe9e4atagen2026-10-22 12:30 UTC1393
        Ok(String::from(response.content_uri))
7ff8adafe9e4atagen2026-10-22 12:30 UTC1394
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1395
7ff8adafe9e4atagen2026-10-22 12:30 UTC1396
    pub async fn get_media_content(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1397
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1398
        media_source: Arc<MediaSource>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1399
    ) -> Result<Vec<u8>, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1400
        let source = (*media_source).clone().media_source;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1401
7ff8adafe9e4atagen2026-10-22 12:30 UTC1402
        debug!(?source, "requesting media file");
7ff8adafe9e4atagen2026-10-22 12:30 UTC1403
        Ok(self
7ff8adafe9e4atagen2026-10-22 12:30 UTC1404
            .inner
7ff8adafe9e4atagen2026-10-22 12:30 UTC1405
            .media()
7ff8adafe9e4atagen2026-10-22 12:30 UTC1406
            .get_media_content(&MediaRequestParameters { source, format: MediaFormat::File }, true)
7ff8adafe9e4atagen2026-10-22 12:30 UTC1407
            .await?)
7ff8adafe9e4atagen2026-10-22 12:30 UTC1408
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1409
7ff8adafe9e4atagen2026-10-22 12:30 UTC1410
    pub async fn get_media_thumbnail(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1411
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1412
        media_source: Arc<MediaSource>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1413
        width: u64,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1414
        height: u64,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1415
    ) -> Result<Vec<u8>, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1416
        let source = (*media_source).clone().media_source;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1417
7ff8adafe9e4atagen2026-10-22 12:30 UTC1418
        debug!(?source, width, height, "requesting media thumbnail");
7ff8adafe9e4atagen2026-10-22 12:30 UTC1419
        Ok(self
7ff8adafe9e4atagen2026-10-22 12:30 UTC1420
            .inner
7ff8adafe9e4atagen2026-10-22 12:30 UTC1421
            .media()
7ff8adafe9e4atagen2026-10-22 12:30 UTC1422
            .get_media_content(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1423
                &MediaRequestParameters {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1424
                    source,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1425
                    format: MediaFormat::Thumbnail(MediaThumbnailSettings::new(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1426
                        UInt::new(width).unwrap(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1427
                        UInt::new(height).unwrap(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1428
                    )),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1429
                },
7ff8adafe9e4atagen2026-10-22 12:30 UTC1430
                true,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1431
            )
7ff8adafe9e4atagen2026-10-22 12:30 UTC1432
            .await?)
7ff8adafe9e4atagen2026-10-22 12:30 UTC1433
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1434
7ff8adafe9e4atagen2026-10-22 12:30 UTC1435
    pub async fn get_session_verification_controller(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1436
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1437
    ) -> Result<Arc<SessionVerificationController>, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1438
        if let Some(session_verification_controller) =
7ff8adafe9e4atagen2026-10-22 12:30 UTC1439
            &*self.session_verification_controller.read().await
7ff8adafe9e4atagen2026-10-22 12:30 UTC1440
        {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1441
            return Ok(Arc::new(session_verification_controller.clone()));
7ff8adafe9e4atagen2026-10-22 12:30 UTC1442
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1443
        let user_id = self.inner.user_id().context("Failed retrieving current user_id")?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1444
        let user_identity = self
7ff8adafe9e4atagen2026-10-22 12:30 UTC1445
            .inner
7ff8adafe9e4atagen2026-10-22 12:30 UTC1446
            .encryption()
7ff8adafe9e4atagen2026-10-22 12:30 UTC1447
            .get_user_identity(user_id)
7ff8adafe9e4atagen2026-10-22 12:30 UTC1448
            .await?
7ff8adafe9e4atagen2026-10-22 12:30 UTC1449
            .context("Failed retrieving user identity")?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1450
7ff8adafe9e4atagen2026-10-22 12:30 UTC1451
        let session_verification_controller = SessionVerificationController::new(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1452
            self.inner.encryption(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1453
            user_identity,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1454
            self.inner.account(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1455
        );
7ff8adafe9e4atagen2026-10-22 12:30 UTC1456
7ff8adafe9e4atagen2026-10-22 12:30 UTC1457
        *self.session_verification_controller.write().await =
7ff8adafe9e4atagen2026-10-22 12:30 UTC1458
            Some(session_verification_controller.clone());
7ff8adafe9e4atagen2026-10-22 12:30 UTC1459
7ff8adafe9e4atagen2026-10-22 12:30 UTC1460
        Ok(Arc::new(session_verification_controller))
7ff8adafe9e4atagen2026-10-22 12:30 UTC1461
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1462
7ff8adafe9e4atagen2026-10-22 12:30 UTC1463
    /// Log the current user out.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1464
    pub async fn logout(&self) -> Result<(), ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1465
        Ok(self.inner.logout().await?)
7ff8adafe9e4atagen2026-10-22 12:30 UTC1466
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1467
7ff8adafe9e4atagen2026-10-22 12:30 UTC1468
    /// Registers a pusher with given parameters
7ff8adafe9e4atagen2026-10-22 12:30 UTC1469
    #[allow(clippy::too_many_arguments)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC1470
    pub async fn set_pusher(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1471
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1472
        identifiers: PusherIdentifiers,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1473
        kind: PusherKind,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1474
        app_display_name: String,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1475
        device_display_name: String,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1476
        profile_tag: Option<String>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1477
        lang: String,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1478
        append: bool,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1479
    ) -> Result<(), ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1480
        let ids = identifiers.into();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1481
7ff8adafe9e4atagen2026-10-22 12:30 UTC1482
        let pusher_init = PusherInit {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1483
            ids,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1484
            kind: kind.try_into()?,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1485
            app_display_name,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1486
            device_display_name,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1487
            profile_tag,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1488
            lang,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1489
        };
7ff8adafe9e4atagen2026-10-22 12:30 UTC1490
        self.inner.pusher().set(pusher_init.into(), append).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1491
        Ok(())
7ff8adafe9e4atagen2026-10-22 12:30 UTC1492
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1493
7ff8adafe9e4atagen2026-10-22 12:30 UTC1494
    /// Deletes a pusher of given pusher ids
7ff8adafe9e4atagen2026-10-22 12:30 UTC1495
    pub async fn delete_pusher(&self, identifiers: PusherIdentifiers) -> Result<(), ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1496
        self.inner.pusher().delete(identifiers.into()).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1497
        Ok(())
7ff8adafe9e4atagen2026-10-22 12:30 UTC1498
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1499
7ff8adafe9e4atagen2026-10-22 12:30 UTC1500
    /// The homeserver this client is configured to use.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1501
    pub fn homeserver(&self) -> String {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1502
        self.inner.homeserver().to_string()
7ff8adafe9e4atagen2026-10-22 12:30 UTC1503
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1504
7ff8adafe9e4atagen2026-10-22 12:30 UTC1505
    /// The URL of the server.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1506
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC1507
    /// Not to be confused with the `Self::homeserver`. `server` is usually
7ff8adafe9e4atagen2026-10-22 12:30 UTC1508
    /// the server part in a user ID, e.g. with `@mnt_io:matrix.org`, here
7ff8adafe9e4atagen2026-10-22 12:30 UTC1509
    /// `matrix.org` is the server, whilst `matrix-client.matrix.org` is the
7ff8adafe9e4atagen2026-10-22 12:30 UTC1510
    /// homeserver (at the time of writing — 2024-08-28).
7ff8adafe9e4atagen2026-10-22 12:30 UTC1511
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC1512
    /// This value is optional depending on how the `Client` has been built.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1513
    /// If it's been built from a homeserver URL directly, we don't know the
7ff8adafe9e4atagen2026-10-22 12:30 UTC1514
    /// server. However, if the `Client` has been built from a server URL or
7ff8adafe9e4atagen2026-10-22 12:30 UTC1515
    /// name, then the homeserver has been discovered, and we know both.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1516
    pub fn server(&self) -> Option<String> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1517
        self.inner.server().map(ToString::to_string)
7ff8adafe9e4atagen2026-10-22 12:30 UTC1518
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1519
7ff8adafe9e4atagen2026-10-22 12:30 UTC1520
    pub fn rooms(&self) -> Vec<Arc<Room>> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1521
        self.inner
7ff8adafe9e4atagen2026-10-22 12:30 UTC1522
            .rooms()
7ff8adafe9e4atagen2026-10-22 12:30 UTC1523
            .into_iter()
7ff8adafe9e4atagen2026-10-22 12:30 UTC1524
            .map(|room| Arc::new(Room::new(room, self.utd_hook_manager.get().cloned())))
7ff8adafe9e4atagen2026-10-22 12:30 UTC1525
            .collect()
7ff8adafe9e4atagen2026-10-22 12:30 UTC1526
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1527
7ff8adafe9e4atagen2026-10-22 12:30 UTC1528
    /// Mark all joined rooms as read by sending public, private and fully-read
7ff8adafe9e4atagen2026-10-22 12:30 UTC1529
    /// receipts on each room's latest event.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1530
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC1531
    /// This is a best-effort operation — per-room errors are logged and
7ff8adafe9e4atagen2026-10-22 12:30 UTC1532
    /// skipped. Receipts are sent unthreaded, which per the Matrix spec
7ff8adafe9e4atagen2026-10-22 12:30 UTC1533
    /// covers all events in a room including those inside threads.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1534
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC1535
    /// This is useful to mitigate backend led wrong iOS app badges and work
7ff8adafe9e4atagen2026-10-22 12:30 UTC1536
    /// around https://github.com/element-hq/element-x-ios/issues/3151
7ff8adafe9e4atagen2026-10-22 12:30 UTC1537
    pub async fn mark_all_rooms_as_read(&self) -> Result<(), ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1538
        use matrix_sdk::room::Receipts;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1539
        use matrix_sdk_base::RoomStateFilter;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1540
        use matrix_sdk_ui::timeline::{TimelineBuilder, TimelineFocus};
7ff8adafe9e4atagen2026-10-22 12:30 UTC1541
7ff8adafe9e4atagen2026-10-22 12:30 UTC1542
        for sdk_room in self.inner.rooms_filtered(RoomStateFilter::JOINED) {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1543
            let timeline = match TimelineBuilder::new(&sdk_room)
7ff8adafe9e4atagen2026-10-22 12:30 UTC1544
                .with_focus(TimelineFocus::Live { hide_threaded_events: false })
7ff8adafe9e4atagen2026-10-22 12:30 UTC1545
                .build()
7ff8adafe9e4atagen2026-10-22 12:30 UTC1546
                .await
7ff8adafe9e4atagen2026-10-22 12:30 UTC1547
            {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1548
                Ok(tl) => tl,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1549
                Err(err) => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1550
                    warn!(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1551
                        "mark_all_rooms_as_read: failed to build timeline for {}: {err}",
7ff8adafe9e4atagen2026-10-22 12:30 UTC1552
                        sdk_room.room_id()
7ff8adafe9e4atagen2026-10-22 12:30 UTC1553
                    );
7ff8adafe9e4atagen2026-10-22 12:30 UTC1554
                    continue;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1555
                }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1556
            };
7ff8adafe9e4atagen2026-10-22 12:30 UTC1557
7ff8adafe9e4atagen2026-10-22 12:30 UTC1558
            if let Some(event_id) = timeline.latest_event_id().await {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1559
                let receipts = Receipts::new()
7ff8adafe9e4atagen2026-10-22 12:30 UTC1560
                    .fully_read_marker(event_id.clone())
7ff8adafe9e4atagen2026-10-22 12:30 UTC1561
                    .private_read_receipt(event_id);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1562
                if let Err(err) = sdk_room.send_multiple_receipts(receipts).await {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1563
                    warn!(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1564
                        "mark_all_rooms_as_read: failed to send receipts for {}: {err}",
7ff8adafe9e4atagen2026-10-22 12:30 UTC1565
                        sdk_room.room_id()
7ff8adafe9e4atagen2026-10-22 12:30 UTC1566
                    );
7ff8adafe9e4atagen2026-10-22 12:30 UTC1567
                }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1568
            } else {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1569
                // Room has no events; just clear any stale explicit unread flag.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1570
                if let Err(err) = sdk_room.set_unread_flag(false).await {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1571
                    warn!(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1572
                        "mark_all_rooms_as_read: failed to clear unread flag for {}: {err}",
7ff8adafe9e4atagen2026-10-22 12:30 UTC1573
                        sdk_room.room_id()
7ff8adafe9e4atagen2026-10-22 12:30 UTC1574
                    );
7ff8adafe9e4atagen2026-10-22 12:30 UTC1575
                }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1576
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1577
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1578
7ff8adafe9e4atagen2026-10-22 12:30 UTC1579
        Ok(())
7ff8adafe9e4atagen2026-10-22 12:30 UTC1580
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1581
7ff8adafe9e4atagen2026-10-22 12:30 UTC1582
    /// Get a room by its ID.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1583
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC1584
    /// # Arguments
7ff8adafe9e4atagen2026-10-22 12:30 UTC1585
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC1586
    /// * `room_id` - The ID of the room to get.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1587
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC1588
    /// # Returns
7ff8adafe9e4atagen2026-10-22 12:30 UTC1589
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC1590
    /// A `Result` containing an optional room, or a `ClientError`.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1591
    /// This method will not initialize the room's timeline or populate it with
7ff8adafe9e4atagen2026-10-22 12:30 UTC1592
    /// events.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1593
    pub fn get_room(&self, room_id: String) -> Result<Option<Arc<Room>>, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1594
        let room_id = RoomId::parse(room_id)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1595
        let sdk_room = self.inner.get_room(&room_id);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1596
7ff8adafe9e4atagen2026-10-22 12:30 UTC1597
        let room =
7ff8adafe9e4atagen2026-10-22 12:30 UTC1598
            sdk_room.map(|room| Arc::new(Room::new(room, self.utd_hook_manager.get().cloned())));
7ff8adafe9e4atagen2026-10-22 12:30 UTC1599
        Ok(room)
7ff8adafe9e4atagen2026-10-22 12:30 UTC1600
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1601
7ff8adafe9e4atagen2026-10-22 12:30 UTC1602
    /// Get the first existing DM room with the given user, if any.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1603
    pub fn get_dm_room(&self, user_id: String) -> Result<Option<Arc<Room>>, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1604
        let user_id = UserId::parse(user_id)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1605
        let sdk_room = self.inner.get_dm_room(&user_id);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1606
        let dm =
7ff8adafe9e4atagen2026-10-22 12:30 UTC1607
            sdk_room.map(|room| Arc::new(Room::new(room, self.utd_hook_manager.get().cloned())));
7ff8adafe9e4atagen2026-10-22 12:30 UTC1608
        Ok(dm)
7ff8adafe9e4atagen2026-10-22 12:30 UTC1609
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1610
7ff8adafe9e4atagen2026-10-22 12:30 UTC1611
    /// Get an iterator with the existing DM rooms for the given user.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1612
    pub fn get_dm_rooms(&self, user_id: String) -> Result<Vec<Arc<Room>>, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1613
        let user_id = UserId::parse(user_id)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1614
        let sdk_rooms = self.inner.get_dm_rooms(&user_id);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1615
        let dms = sdk_rooms
7ff8adafe9e4atagen2026-10-22 12:30 UTC1616
            .map(|room| Arc::new(Room::new(room, self.utd_hook_manager.get().cloned())))
7ff8adafe9e4atagen2026-10-22 12:30 UTC1617
            .collect();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1618
        Ok(dms)
7ff8adafe9e4atagen2026-10-22 12:30 UTC1619
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1620
7ff8adafe9e4atagen2026-10-22 12:30 UTC1621
    pub async fn search_users(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1622
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1623
        search_term: String,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1624
        limit: u64,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1625
    ) -> Result<SearchUsersResults, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1626
        let response = self.inner.search_users(&search_term, limit).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1627
        Ok(SearchUsersResults::from(response))
7ff8adafe9e4atagen2026-10-22 12:30 UTC1628
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1629
7ff8adafe9e4atagen2026-10-22 12:30 UTC1630
    pub async fn get_profile(&self, user_id: String) -> Result<UserProfile, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1631
        let user_id = <&UserId>::try_from(user_id.as_str())?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1632
        UserProfile::fetch(&self.inner.account(), user_id).await
7ff8adafe9e4atagen2026-10-22 12:30 UTC1633
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1634
7ff8adafe9e4atagen2026-10-22 12:30 UTC1635
    pub async fn notification_client(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1636
        self: Arc<Self>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1637
        process_setup: NotificationProcessSetup,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1638
    ) -> Result<Arc<NotificationClient>, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1639
        Ok(Arc::new(NotificationClient {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1640
            inner: MatrixNotificationClient::new((*self.inner).clone(), process_setup.into())
7ff8adafe9e4atagen2026-10-22 12:30 UTC1641
                .await?,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1642
            client: self.clone(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1643
        }))
7ff8adafe9e4atagen2026-10-22 12:30 UTC1644
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1645
7ff8adafe9e4atagen2026-10-22 12:30 UTC1646
    pub fn sync_service(&self) -> Arc<SyncServiceBuilder> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1647
        SyncServiceBuilder::new((*self.inner).clone(), self.utd_hook_manager.get().cloned())
7ff8adafe9e4atagen2026-10-22 12:30 UTC1648
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1649
7ff8adafe9e4atagen2026-10-22 12:30 UTC1650
    /// Start a sync v2 loop.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1651
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC1652
    /// This is an alternative to [`Client::sync_service`] (which uses Sliding
7ff8adafe9e4atagen2026-10-22 12:30 UTC1653
    /// Sync / MSC4186). It works with any homeserver, including older
7ff8adafe9e4atagen2026-10-22 12:30 UTC1654
    /// Synapse versions that do not support Sliding Sync.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1655
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC1656
    /// Returns a `TaskHandle` that can be used to cancel the sync loop.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1657
    /// The listener is called after each successful sync response.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1658
    pub fn sync_v2(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1659
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1660
        settings: SyncSettingsV2,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1661
        listener: Box<dyn SyncListenerV2>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1662
    ) -> Arc<TaskHandle> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1663
        let client = (*self.inner).clone();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1664
        let sdk_settings: matrix_sdk::config::SyncSettings = settings.into();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1665
        let listener: Arc<dyn SyncListenerV2> = Arc::from(listener);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1666
7ff8adafe9e4atagen2026-10-22 12:30 UTC1667
        Arc::new(TaskHandle::new(get_runtime_handle().spawn(async move {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1668
            let result = client
7ff8adafe9e4atagen2026-10-22 12:30 UTC1669
                .sync_with_result_callback(sdk_settings, |result| {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1670
                    let listener = listener.clone();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1671
                    async move {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1672
                        let response = result?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1673
                        let ffi_response: SyncResponseV2 = response.into();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1674
                        listener.on_update(ffi_response);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1675
                        Ok(matrix_sdk::LoopCtrl::Continue)
7ff8adafe9e4atagen2026-10-22 12:30 UTC1676
                    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1677
                })
7ff8adafe9e4atagen2026-10-22 12:30 UTC1678
                .await;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1679
7ff8adafe9e4atagen2026-10-22 12:30 UTC1680
            if let Err(e) = result {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1681
                tracing::error!("Sync loop ended with error: {e}");
7ff8adafe9e4atagen2026-10-22 12:30 UTC1682
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1683
        })))
7ff8adafe9e4atagen2026-10-22 12:30 UTC1684
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1685
7ff8adafe9e4atagen2026-10-22 12:30 UTC1686
    /// Perform a single sync v2 call.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1687
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC1688
    /// This is useful for performing an initial sync or a one-shot sync
7ff8adafe9e4atagen2026-10-22 12:30 UTC1689
    /// without entering a continuous loop.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1690
    pub async fn sync_once_v2(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1691
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1692
        settings: SyncSettingsV2,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1693
    ) -> Result<SyncResponseV2, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1694
        let sdk_settings: matrix_sdk::config::SyncSettings = settings.into();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1695
        let response = self.inner.sync_once(sdk_settings).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1696
        Ok(response.into())
7ff8adafe9e4atagen2026-10-22 12:30 UTC1697
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1698
7ff8adafe9e4atagen2026-10-22 12:30 UTC1699
    pub async fn space_service(&self) -> Arc<SpaceService> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1700
        let inner = UISpaceService::new((*self.inner).clone()).await;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1701
        Arc::new(SpaceService::new(inner))
7ff8adafe9e4atagen2026-10-22 12:30 UTC1702
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1703
7ff8adafe9e4atagen2026-10-22 12:30 UTC1704
    pub async fn get_notification_settings(&self) -> Arc<NotificationSettings> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1705
        let inner = self.inner.notification_settings().await;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1706
7ff8adafe9e4atagen2026-10-22 12:30 UTC1707
        Arc::new(NotificationSettings::new((*self.inner).clone(), inner))
7ff8adafe9e4atagen2026-10-22 12:30 UTC1708
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1709
7ff8adafe9e4atagen2026-10-22 12:30 UTC1710
    pub fn encryption(self: Arc<Self>) -> Arc<Encryption> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1711
        Arc::new(Encryption { inner: self.inner.encryption(), _client: self.clone() })
7ff8adafe9e4atagen2026-10-22 12:30 UTC1712
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1713
7ff8adafe9e4atagen2026-10-22 12:30 UTC1714
    // Ignored users
7ff8adafe9e4atagen2026-10-22 12:30 UTC1715
7ff8adafe9e4atagen2026-10-22 12:30 UTC1716
    pub async fn ignored_users(&self) -> Result<Vec<String>, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1717
        if let Some(raw_content) =
7ff8adafe9e4atagen2026-10-22 12:30 UTC1718
            self.inner.account().fetch_account_data_static::<IgnoredUserListEventContent>().await?
7ff8adafe9e4atagen2026-10-22 12:30 UTC1719
        {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1720
            let content = raw_content.deserialize()?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1721
            let user_ids: Vec<String> =
7ff8adafe9e4atagen2026-10-22 12:30 UTC1722
                content.ignored_users.keys().map(|id| id.to_string()).collect();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1723
7ff8adafe9e4atagen2026-10-22 12:30 UTC1724
            return Ok(user_ids);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1725
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1726
7ff8adafe9e4atagen2026-10-22 12:30 UTC1727
        Ok(vec![])
7ff8adafe9e4atagen2026-10-22 12:30 UTC1728
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1729
7ff8adafe9e4atagen2026-10-22 12:30 UTC1730
    pub async fn ignore_user(&self, user_id: String) -> Result<(), ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1731
        let user_id = UserId::parse(user_id)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1732
        self.inner.account().ignore_user(&user_id).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1733
        Ok(())
7ff8adafe9e4atagen2026-10-22 12:30 UTC1734
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1735
7ff8adafe9e4atagen2026-10-22 12:30 UTC1736
    pub async fn unignore_user(&self, user_id: String) -> Result<(), ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1737
        let user_id = UserId::parse(user_id)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1738
        self.inner.account().unignore_user(&user_id).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1739
        Ok(())
7ff8adafe9e4atagen2026-10-22 12:30 UTC1740
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1741
7ff8adafe9e4atagen2026-10-22 12:30 UTC1742
    pub fn subscribe_to_ignored_users(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1743
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1744
        listener: Box<dyn IgnoredUsersListener>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1745
    ) -> Arc<TaskHandle> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1746
        let mut subscriber = self.inner.subscribe_to_ignore_user_list_changes();
7ff8adafe9e4atagen2026-10-22 12:30 UTC1747
        Arc::new(TaskHandle::new(get_runtime_handle().spawn(async move {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1748
            while let Some(user_ids) = subscriber.next().await {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1749
                listener.call(user_ids);
7ff8adafe9e4atagen2026-10-22 12:30 UTC1750
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1751
        })))
7ff8adafe9e4atagen2026-10-22 12:30 UTC1752
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1753
7ff8adafe9e4atagen2026-10-22 12:30 UTC1754
    pub fn room_directory_search(&self) -> Arc<RoomDirectorySearch> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1755
        Arc::new(RoomDirectorySearch::new(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1756
            matrix_sdk::room_directory_search::RoomDirectorySearch::new((*self.inner).clone()),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1757
        ))
7ff8adafe9e4atagen2026-10-22 12:30 UTC1758
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1759
7ff8adafe9e4atagen2026-10-22 12:30 UTC1760
    /// Join a room by its ID.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1761
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC1762
    /// Use this method when the homeserver already knows of the given room ID.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1763
    /// Otherwise use `join_room_by_id_or_alias` so you can pass a list of
7ff8adafe9e4atagen2026-10-22 12:30 UTC1764
    /// server names for the homeserver to find the room.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1765
    pub async fn join_room_by_id(&self, room_id: String) -> Result<Arc<Room>, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1766
        let room_id = RoomId::parse(room_id)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1767
        let room = self.inner.join_room_by_id(room_id.as_ref()).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1768
        Ok(Arc::new(Room::new(room, self.utd_hook_manager.get().cloned())))
7ff8adafe9e4atagen2026-10-22 12:30 UTC1769
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1770
7ff8adafe9e4atagen2026-10-22 12:30 UTC1771
    /// Join a room by its ID or alias.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1772
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC1773
    /// When supplying the room's ID, you can also supply a list of server names
7ff8adafe9e4atagen2026-10-22 12:30 UTC1774
    /// for the homeserver to find the room. Typically these server names
7ff8adafe9e4atagen2026-10-22 12:30 UTC1775
    /// come from a permalink's `via` parameters, or from resolving a room's
7ff8adafe9e4atagen2026-10-22 12:30 UTC1776
    /// alias into an ID.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1777
    pub async fn join_room_by_id_or_alias(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1778
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1779
        room_id_or_alias: String,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1780
        server_names: Vec<String>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1781
    ) -> Result<Arc<Room>, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1782
        let room_id = RoomOrAliasId::parse(&room_id_or_alias)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1783
        let server_names = server_names
7ff8adafe9e4atagen2026-10-22 12:30 UTC1784
            .iter()
7ff8adafe9e4atagen2026-10-22 12:30 UTC1785
            .map(|name| OwnedServerName::try_from(name.as_str()))
7ff8adafe9e4atagen2026-10-22 12:30 UTC1786
            .collect::<Result<Vec<_>, _>>()?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1787
        let room =
7ff8adafe9e4atagen2026-10-22 12:30 UTC1788
            self.inner.join_room_by_id_or_alias(room_id.as_ref(), server_names.as_ref()).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1789
        Ok(Arc::new(Room::new(room, self.utd_hook_manager.get().cloned())))
7ff8adafe9e4atagen2026-10-22 12:30 UTC1790
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1791
7ff8adafe9e4atagen2026-10-22 12:30 UTC1792
    /// Knock on a room to join it using its ID or alias.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1793
    pub async fn knock(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1794
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1795
        room_id_or_alias: String,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1796
        reason: Option<String>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1797
        server_names: Vec<String>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1798
    ) -> Result<Arc<Room>, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1799
        let room_id = RoomOrAliasId::parse(&room_id_or_alias)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1800
        let server_names =
7ff8adafe9e4atagen2026-10-22 12:30 UTC1801
            server_names.iter().map(ServerName::parse).collect::<Result<Vec<_>, _>>()?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1802
        let room = self.inner.knock(room_id, reason, server_names).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1803
        Ok(Arc::new(Room::new(room, self.utd_hook_manager.get().cloned())))
7ff8adafe9e4atagen2026-10-22 12:30 UTC1804
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1805
7ff8adafe9e4atagen2026-10-22 12:30 UTC1806
    pub async fn get_recently_visited_rooms(&self) -> Result<Vec<String>, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1807
        Ok(self
7ff8adafe9e4atagen2026-10-22 12:30 UTC1808
            .inner
7ff8adafe9e4atagen2026-10-22 12:30 UTC1809
            .account()
7ff8adafe9e4atagen2026-10-22 12:30 UTC1810
            .get_recently_visited_rooms()
7ff8adafe9e4atagen2026-10-22 12:30 UTC1811
            .await?
7ff8adafe9e4atagen2026-10-22 12:30 UTC1812
            .into_iter()
7ff8adafe9e4atagen2026-10-22 12:30 UTC1813
            .map(Into::into)
7ff8adafe9e4atagen2026-10-22 12:30 UTC1814
            .collect())
7ff8adafe9e4atagen2026-10-22 12:30 UTC1815
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1816
7ff8adafe9e4atagen2026-10-22 12:30 UTC1817
    pub async fn track_recently_visited_room(&self, room: String) -> Result<(), ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1818
        let room_id = RoomId::parse(room)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1819
        self.inner.account().track_recently_visited_room(room_id).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1820
        Ok(())
7ff8adafe9e4atagen2026-10-22 12:30 UTC1821
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1822
7ff8adafe9e4atagen2026-10-22 12:30 UTC1823
    /// Resolves the given room alias to a room ID (and a list of servers), if
7ff8adafe9e4atagen2026-10-22 12:30 UTC1824
    /// possible.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1825
    pub async fn resolve_room_alias(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1826
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1827
        room_alias: String,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1828
    ) -> Result<Option<ResolvedRoomAlias>, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1829
        let room_alias = RoomAliasId::parse(&room_alias)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1830
        match self.inner.resolve_room_alias(&room_alias).await {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1831
            Ok(response) => Ok(Some(response.into())),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1832
            Err(error) => match error.client_api_error_kind() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1833
                // The room alias wasn't found, so we return None.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1834
                Some(ErrorKind::NotFound) => Ok(None),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1835
                // In any other case we just return the error, mapped.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1836
                _ => Err(error.into()),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1837
            },
7ff8adafe9e4atagen2026-10-22 12:30 UTC1838
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1839
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1840
7ff8adafe9e4atagen2026-10-22 12:30 UTC1841
    /// Checks if a room alias exists in the current homeserver.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1842
    pub async fn room_alias_exists(&self, room_alias: String) -> Result<bool, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1843
        self.resolve_room_alias(room_alias).await.map(|ret| ret.is_some())
7ff8adafe9e4atagen2026-10-22 12:30 UTC1844
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1845
7ff8adafe9e4atagen2026-10-22 12:30 UTC1846
    /// Given a room id, get the preview of a room, to interact with it.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1847
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC1848
    /// The list of `via_servers` must be a list of servers that know
7ff8adafe9e4atagen2026-10-22 12:30 UTC1849
    /// about the room and can resolve it, and that may appear as a `via`
7ff8adafe9e4atagen2026-10-22 12:30 UTC1850
    /// parameter in e.g. a permalink URL. This list can be empty.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1851
    pub async fn get_room_preview_from_room_id(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1852
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1853
        room_id: String,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1854
        via_servers: Vec<String>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1855
    ) -> Result<Arc<RoomPreview>, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1856
        let room_id = RoomId::parse(&room_id).context("room_id is not a valid room id")?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1857
7ff8adafe9e4atagen2026-10-22 12:30 UTC1858
        let via_servers = via_servers
7ff8adafe9e4atagen2026-10-22 12:30 UTC1859
            .into_iter()
7ff8adafe9e4atagen2026-10-22 12:30 UTC1860
            .map(ServerName::parse)
7ff8adafe9e4atagen2026-10-22 12:30 UTC1861
            .collect::<Result<Vec<_>, _>>()
7ff8adafe9e4atagen2026-10-22 12:30 UTC1862
            .context("at least one `via` server name is invalid")?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1863
7ff8adafe9e4atagen2026-10-22 12:30 UTC1864
        // The `into()` call below doesn't work if I do `(&room_id).into()`, so I let
7ff8adafe9e4atagen2026-10-22 12:30 UTC1865
        // rustc win that one fight.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1866
        let room_id: &RoomId = &room_id;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1867
7ff8adafe9e4atagen2026-10-22 12:30 UTC1868
        let room_preview = self.inner.get_room_preview(room_id.into(), via_servers).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1869
7ff8adafe9e4atagen2026-10-22 12:30 UTC1870
        Ok(Arc::new(RoomPreview::new(self.inner.clone(), room_preview)))
7ff8adafe9e4atagen2026-10-22 12:30 UTC1871
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1872
7ff8adafe9e4atagen2026-10-22 12:30 UTC1873
    /// Given a room alias, get the preview of a room, to interact with it.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1874
    pub async fn get_room_preview_from_room_alias(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1875
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1876
        room_alias: String,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1877
    ) -> Result<Arc<RoomPreview>, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1878
        let room_alias =
7ff8adafe9e4atagen2026-10-22 12:30 UTC1879
            RoomAliasId::parse(&room_alias).context("room_alias is not a valid room alias")?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1880
7ff8adafe9e4atagen2026-10-22 12:30 UTC1881
        // The `into()` call below doesn't work if I do `(&room_id).into()`, so I let
7ff8adafe9e4atagen2026-10-22 12:30 UTC1882
        // rustc win that one fight.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1883
        let room_alias: &RoomAliasId = &room_alias;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1884
7ff8adafe9e4atagen2026-10-22 12:30 UTC1885
        let room_preview = self.inner.get_room_preview(room_alias.into(), Vec::new()).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1886
7ff8adafe9e4atagen2026-10-22 12:30 UTC1887
        Ok(Arc::new(RoomPreview::new(self.inner.clone(), room_preview)))
7ff8adafe9e4atagen2026-10-22 12:30 UTC1888
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1889
7ff8adafe9e4atagen2026-10-22 12:30 UTC1890
    /// Waits until an at least partially synced room is received, and returns
7ff8adafe9e4atagen2026-10-22 12:30 UTC1891
    /// it.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1892
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC1893
    /// **Note: this function will loop endlessly until either it finds the room
7ff8adafe9e4atagen2026-10-22 12:30 UTC1894
    /// or an externally set timeout happens.**
7ff8adafe9e4atagen2026-10-22 12:30 UTC1895
    pub async fn await_room_remote_echo(&self, room_id: String) -> Result<Arc<Room>, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1896
        let room_id = RoomId::parse(room_id)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1897
        Ok(Arc::new(Room::new(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1898
            self.inner.await_room_remote_echo(&room_id).await,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1899
            self.utd_hook_manager.get().cloned(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC1900
        )))
7ff8adafe9e4atagen2026-10-22 12:30 UTC1901
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1902
7ff8adafe9e4atagen2026-10-22 12:30 UTC1903
    /// Lets the user know whether this is an `m.login.password` based
7ff8adafe9e4atagen2026-10-22 12:30 UTC1904
    /// auth and if the account can actually be deactivated
7ff8adafe9e4atagen2026-10-22 12:30 UTC1905
    pub fn can_deactivate_account(&self) -> bool {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1906
        matches!(self.inner.auth_api(), Some(AuthApi::Matrix(_)))
7ff8adafe9e4atagen2026-10-22 12:30 UTC1907
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1908
7ff8adafe9e4atagen2026-10-22 12:30 UTC1909
    /// Deactivate this account definitively.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1910
    /// Similarly to `encryption::reset_identity` this
7ff8adafe9e4atagen2026-10-22 12:30 UTC1911
    /// will only work with password-based authentication (`m.login.password`)
7ff8adafe9e4atagen2026-10-22 12:30 UTC1912
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC1913
    /// # Arguments
7ff8adafe9e4atagen2026-10-22 12:30 UTC1914
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC1915
    /// * `auth_data` - This request uses the [User-Interactive Authentication
7ff8adafe9e4atagen2026-10-22 12:30 UTC1916
    ///   API][uiaa]. The first request needs to set this to `None` and will
7ff8adafe9e4atagen2026-10-22 12:30 UTC1917
    ///   always fail and the same request needs to be made but this time with
7ff8adafe9e4atagen2026-10-22 12:30 UTC1918
    ///   some `auth_data` provided.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1919
    pub async fn deactivate_account(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1920
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1921
        auth_data: Option<AuthData>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1922
        erase_data: bool,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1923
    ) -> Result<(), ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1924
        if let Some(auth_data) = auth_data {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1925
            _ = self.inner.account().deactivate(None, Some(auth_data.into()), erase_data).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1926
        } else {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1927
            _ = self.inner.account().deactivate(None, None, erase_data).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1928
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1929
7ff8adafe9e4atagen2026-10-22 12:30 UTC1930
        Ok(())
7ff8adafe9e4atagen2026-10-22 12:30 UTC1931
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1932
7ff8adafe9e4atagen2026-10-22 12:30 UTC1933
    /// Checks if a room alias is not in use yet.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1934
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC1935
    /// Returns:
7ff8adafe9e4atagen2026-10-22 12:30 UTC1936
    /// - `Ok(true)` if the room alias is available.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1937
    /// - `Ok(false)` if it's not (the resolve alias request returned a `404`
7ff8adafe9e4atagen2026-10-22 12:30 UTC1938
    ///   status code).
7ff8adafe9e4atagen2026-10-22 12:30 UTC1939
    /// - An `Err` otherwise.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1940
    pub async fn is_room_alias_available(&self, alias: String) -> Result<bool, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1941
        let alias = RoomAliasId::parse(alias)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1942
        self.inner.is_room_alias_available(&alias).await.map_err(Into::into)
7ff8adafe9e4atagen2026-10-22 12:30 UTC1943
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1944
7ff8adafe9e4atagen2026-10-22 12:30 UTC1945
    /// Set the media retention policy.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1946
    pub async fn set_media_retention_policy(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1947
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1948
        policy: MediaRetentionPolicy,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1949
    ) -> Result<(), ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1950
        let closure = async || -> Result<_, Error> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1951
            let store = self.inner.media_store().lock().await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1952
            Ok(store.set_media_retention_policy(policy).await?)
7ff8adafe9e4atagen2026-10-22 12:30 UTC1953
        };
7ff8adafe9e4atagen2026-10-22 12:30 UTC1954
7ff8adafe9e4atagen2026-10-22 12:30 UTC1955
        Ok(closure().await?)
7ff8adafe9e4atagen2026-10-22 12:30 UTC1956
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1957
7ff8adafe9e4atagen2026-10-22 12:30 UTC1958
    /// Clear all the non-critical caches for this Client instance.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1959
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC1960
    /// WARNING: This will clear all the caches, including the base store (state
7ff8adafe9e4atagen2026-10-22 12:30 UTC1961
    /// store), so callers must make sure that the Client is at rest before
7ff8adafe9e4atagen2026-10-22 12:30 UTC1962
    /// calling it.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1963
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC1964
    /// In particular, if a [`SyncService`] is running, it must be passed here
7ff8adafe9e4atagen2026-10-22 12:30 UTC1965
    /// as a parameter, or stopped before calling this method. Ideally, the
7ff8adafe9e4atagen2026-10-22 12:30 UTC1966
    /// send queues should have been disabled and must all be inactive (i.e.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1967
    /// not sending events); this method will disable them, but it might not
7ff8adafe9e4atagen2026-10-22 12:30 UTC1968
    /// be enough if the queues are still processing events.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1969
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC1970
    /// After the method returns, the Client will be in an unstable
7ff8adafe9e4atagen2026-10-22 12:30 UTC1971
    /// state, and it is required that the caller reinstantiates a new
7ff8adafe9e4atagen2026-10-22 12:30 UTC1972
    /// Client instance, be it via dropping the previous and re-creating it,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1973
    /// restarting their application, or any other similar means.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1974
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC1975
    /// - This will get rid of the backing state store file, if provided.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1976
    /// - This will empty all the room's persisted event caches, so all rooms
7ff8adafe9e4atagen2026-10-22 12:30 UTC1977
    ///   will start as if they were empty.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1978
    /// - This will empty the media cache according to the current media
7ff8adafe9e4atagen2026-10-22 12:30 UTC1979
    ///   retention policy.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1980
    pub async fn clear_caches(
7ff8adafe9e4atagen2026-10-22 12:30 UTC1981
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1982
        sync_service: Option<Arc<SyncService>>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC1983
    ) -> Result<(), ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1984
        let closure = async || -> Result<_, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1985
            // First, make sure to expire sessions in the sync service.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1986
            if let Some(sync_service) = sync_service {
7ff8adafe9e4atagen2026-10-22 12:30 UTC1987
                sync_service.inner.expire_sessions().await;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1988
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC1989
7ff8adafe9e4atagen2026-10-22 12:30 UTC1990
            // Disable the send queues, as they might read and write to the state store.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1991
            // Events being send might still be active, and cause errors if
7ff8adafe9e4atagen2026-10-22 12:30 UTC1992
            // processing finishes, so this will only minimize damage. Since
7ff8adafe9e4atagen2026-10-22 12:30 UTC1993
            // this method should only be called in exceptional cases, this has
7ff8adafe9e4atagen2026-10-22 12:30 UTC1994
            // been deemed acceptable.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1995
            self.inner.send_queue().set_enabled(false).await;
7ff8adafe9e4atagen2026-10-22 12:30 UTC1996
7ff8adafe9e4atagen2026-10-22 12:30 UTC1997
            // Clean up the media cache according to the current media retention policy.
7ff8adafe9e4atagen2026-10-22 12:30 UTC1998
            self.inner
7ff8adafe9e4atagen2026-10-22 12:30 UTC1999
                .media_store()
7ff8adafe9e4atagen2026-10-22 12:30 UTC2000
                .lock()
7ff8adafe9e4atagen2026-10-22 12:30 UTC2001
                .await
7ff8adafe9e4atagen2026-10-22 12:30 UTC2002
                .map_err(Error::from)?
7ff8adafe9e4atagen2026-10-22 12:30 UTC2003
                .clean()
7ff8adafe9e4atagen2026-10-22 12:30 UTC2004
                .await
7ff8adafe9e4atagen2026-10-22 12:30 UTC2005
                .map_err(Error::from)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC2006
7ff8adafe9e4atagen2026-10-22 12:30 UTC2007
            // Clear all the room chunks. It's important to *not* call
7ff8adafe9e4atagen2026-10-22 12:30 UTC2008
            // `EventCacheStore::clear_all_linked_chunks` here, because there might be live
7ff8adafe9e4atagen2026-10-22 12:30 UTC2009
            // observers of the linked chunks, and that would cause some very bad state
7ff8adafe9e4atagen2026-10-22 12:30 UTC2010
            // mismatch.
7ff8adafe9e4atagen2026-10-22 12:30 UTC2011
            self.inner.event_cache().clear_all_rooms().await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC2012
7ff8adafe9e4atagen2026-10-22 12:30 UTC2013
            // Delete the state store file, if it exists.
7ff8adafe9e4atagen2026-10-22 12:30 UTC2014
            #[cfg(feature = "sqlite")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC2015
            if let Some(store_path) = &self.store_path {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2016
                debug!("Removing the state store: {}", store_path.display());
7ff8adafe9e4atagen2026-10-22 12:30 UTC2017
7ff8adafe9e4atagen2026-10-22 12:30 UTC2018
                // The state store and the crypto store both live in the same store path, so we
7ff8adafe9e4atagen2026-10-22 12:30 UTC2019
                // can't blindly delete the directory.
7ff8adafe9e4atagen2026-10-22 12:30 UTC2020
                //
7ff8adafe9e4atagen2026-10-22 12:30 UTC2021
                // Delete the state store SQLite file, as well as the write-ahead log (WAL) and
7ff8adafe9e4atagen2026-10-22 12:30 UTC2022
                // shared-memory (SHM) files, if they exist.
7ff8adafe9e4atagen2026-10-22 12:30 UTC2023
7ff8adafe9e4atagen2026-10-22 12:30 UTC2024
                for file_name in [
7ff8adafe9e4atagen2026-10-22 12:30 UTC2025
                    PathBuf::from(STATE_STORE_DATABASE_NAME),
7ff8adafe9e4atagen2026-10-22 12:30 UTC2026
                    PathBuf::from(format!("{STATE_STORE_DATABASE_NAME}.wal")),
7ff8adafe9e4atagen2026-10-22 12:30 UTC2027
                    PathBuf::from(format!("{STATE_STORE_DATABASE_NAME}.shm")),
7ff8adafe9e4atagen2026-10-22 12:30 UTC2028
                ] {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2029
                    let file_path = store_path.join(file_name);
7ff8adafe9e4atagen2026-10-22 12:30 UTC2030
                    if file_path.exists() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2031
                        debug!("Removing file: {}", file_path.display());
7ff8adafe9e4atagen2026-10-22 12:30 UTC2032
                        std::fs::remove_file(&file_path).map_err(|err| ClientError::Generic {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2033
                            msg: format!(
7ff8adafe9e4atagen2026-10-22 12:30 UTC2034
                                "couldn't delete the state store file {}: {err}",
7ff8adafe9e4atagen2026-10-22 12:30 UTC2035
                                file_path.display()
7ff8adafe9e4atagen2026-10-22 12:30 UTC2036
                            ),
7ff8adafe9e4atagen2026-10-22 12:30 UTC2037
                            details: None,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2038
                        })?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC2039
                    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2040
                }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2041
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2042
7ff8adafe9e4atagen2026-10-22 12:30 UTC2043
            Ok(())
7ff8adafe9e4atagen2026-10-22 12:30 UTC2044
        };
7ff8adafe9e4atagen2026-10-22 12:30 UTC2045
7ff8adafe9e4atagen2026-10-22 12:30 UTC2046
        closure().await
7ff8adafe9e4atagen2026-10-22 12:30 UTC2047
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2048
7ff8adafe9e4atagen2026-10-22 12:30 UTC2049
    /// Checks if the server supports the report room API.
7ff8adafe9e4atagen2026-10-22 12:30 UTC2050
    pub async fn is_report_room_api_supported(&self) -> Result<bool, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2051
        Ok(self.inner.server_versions().await?.contains(&ruma::api::MatrixVersion::V1_13))
7ff8adafe9e4atagen2026-10-22 12:30 UTC2052
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2053
7ff8adafe9e4atagen2026-10-22 12:30 UTC2054
    /// Checks if the server supports the LiveKit RTC focus for placing calls.
7ff8adafe9e4atagen2026-10-22 12:30 UTC2055
    pub async fn is_livekit_rtc_supported(&self) -> Result<bool, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2056
        Ok(self
7ff8adafe9e4atagen2026-10-22 12:30 UTC2057
            .inner
7ff8adafe9e4atagen2026-10-22 12:30 UTC2058
            .rtc_foci()
7ff8adafe9e4atagen2026-10-22 12:30 UTC2059
            .await?
7ff8adafe9e4atagen2026-10-22 12:30 UTC2060
            .iter()
7ff8adafe9e4atagen2026-10-22 12:30 UTC2061
            .any(|focus| matches!(focus, RtcTransport::LiveKit(_))))
7ff8adafe9e4atagen2026-10-22 12:30 UTC2062
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2063
7ff8adafe9e4atagen2026-10-22 12:30 UTC2064
    /// Get information about the homeserver's advertised map tile server, if
7ff8adafe9e4atagen2026-10-22 12:30 UTC2065
    /// any.
7ff8adafe9e4atagen2026-10-22 12:30 UTC2066
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC2067
    /// Reads the `tile_server` field of the matrix client well-known (MSC3488).
7ff8adafe9e4atagen2026-10-22 12:30 UTC2068
    /// Uses the cached well-known when available, otherwise fetches it from the
7ff8adafe9e4atagen2026-10-22 12:30 UTC2069
    /// homeserver.
7ff8adafe9e4atagen2026-10-22 12:30 UTC2070
    pub async fn tile_server(&self) -> Option<matrix_sdk::TileServerInfo> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2071
        self.inner.tile_server().await
7ff8adafe9e4atagen2026-10-22 12:30 UTC2072
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2073
7ff8adafe9e4atagen2026-10-22 12:30 UTC2074
    /// Checks if the server supports login using a QR code.
7ff8adafe9e4atagen2026-10-22 12:30 UTC2075
    pub async fn is_login_with_qr_code_supported(&self) -> Result<bool, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2076
        Ok(matches!(self.inner.auth_api(), Some(AuthApi::OAuth(_)))
7ff8adafe9e4atagen2026-10-22 12:30 UTC2077
            && self.inner.unstable_features().await?.contains(&ruma::api::FeatureFlag::Msc4108))
7ff8adafe9e4atagen2026-10-22 12:30 UTC2078
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2079
7ff8adafe9e4atagen2026-10-22 12:30 UTC2080
    /// Get server vendor information from the federation API.
7ff8adafe9e4atagen2026-10-22 12:30 UTC2081
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC2082
    /// This method retrieves information about the server's name and version
7ff8adafe9e4atagen2026-10-22 12:30 UTC2083
    /// by calling the `/_matrix/federation/v1/version` endpoint.
7ff8adafe9e4atagen2026-10-22 12:30 UTC2084
    pub async fn server_vendor_info(&self) -> Result<matrix_sdk::ServerVendorInfo, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2085
        Ok(self.inner.server_vendor_info(None).await?)
7ff8adafe9e4atagen2026-10-22 12:30 UTC2086
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2087
7ff8adafe9e4atagen2026-10-22 12:30 UTC2088
    /// Subscribe to changes in the media preview configuration.
7ff8adafe9e4atagen2026-10-22 12:30 UTC2089
    pub async fn subscribe_to_media_preview_config(
7ff8adafe9e4atagen2026-10-22 12:30 UTC2090
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2091
        listener: Box<dyn MediaPreviewConfigListener>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2092
    ) -> Result<Arc<TaskHandle>, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2093
        let (initial_value, stream) = self.inner.account().observe_media_preview_config().await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC2094
        Ok(Arc::new(TaskHandle::new(get_runtime_handle().spawn(async move {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2095
            // Send the initial value to the listener.
7ff8adafe9e4atagen2026-10-22 12:30 UTC2096
            listener.on_change(initial_value.map(|config| config.into()));
7ff8adafe9e4atagen2026-10-22 12:30 UTC2097
            // Listen for changes and notify the listener.
7ff8adafe9e4atagen2026-10-22 12:30 UTC2098
            pin_mut!(stream);
7ff8adafe9e4atagen2026-10-22 12:30 UTC2099
            while let Some(media_preview_config) = stream.next().await {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2100
                listener.on_change(Some(media_preview_config.into()));
7ff8adafe9e4atagen2026-10-22 12:30 UTC2101
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2102
        }))))
7ff8adafe9e4atagen2026-10-22 12:30 UTC2103
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2104
7ff8adafe9e4atagen2026-10-22 12:30 UTC2105
    /// Set the media previews timeline display policy
7ff8adafe9e4atagen2026-10-22 12:30 UTC2106
    pub async fn set_media_preview_display_policy(
7ff8adafe9e4atagen2026-10-22 12:30 UTC2107
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2108
        policy: MediaPreviews,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2109
    ) -> Result<(), ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2110
        self.inner.account().set_media_previews_display_policy(policy.into()).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC2111
        Ok(())
7ff8adafe9e4atagen2026-10-22 12:30 UTC2112
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2113
7ff8adafe9e4atagen2026-10-22 12:30 UTC2114
    /// Get the media previews timeline display policy
7ff8adafe9e4atagen2026-10-22 12:30 UTC2115
    /// currently stored in the cache.
7ff8adafe9e4atagen2026-10-22 12:30 UTC2116
    pub async fn get_media_preview_display_policy(
7ff8adafe9e4atagen2026-10-22 12:30 UTC2117
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2118
    ) -> Result<Option<MediaPreviews>, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2119
        let configuration = self.inner.account().get_media_preview_config_event_content().await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC2120
        match configuration {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2121
            Some(configuration) => Ok(configuration.media_previews.map(Into::into)),
7ff8adafe9e4atagen2026-10-22 12:30 UTC2122
            None => Ok(None),
7ff8adafe9e4atagen2026-10-22 12:30 UTC2123
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2124
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2125
7ff8adafe9e4atagen2026-10-22 12:30 UTC2126
    /// Set the invite request avatars display policy
7ff8adafe9e4atagen2026-10-22 12:30 UTC2127
    pub async fn set_invite_avatars_display_policy(
7ff8adafe9e4atagen2026-10-22 12:30 UTC2128
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2129
        policy: InviteAvatars,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2130
    ) -> Result<(), ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2131
        self.inner.account().set_invite_avatars_display_policy(policy.into()).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC2132
        Ok(())
7ff8adafe9e4atagen2026-10-22 12:30 UTC2133
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2134
7ff8adafe9e4atagen2026-10-22 12:30 UTC2135
    /// Get the invite request avatars display policy
7ff8adafe9e4atagen2026-10-22 12:30 UTC2136
    /// currently stored in the cache.
7ff8adafe9e4atagen2026-10-22 12:30 UTC2137
    pub async fn get_invite_avatars_display_policy(
7ff8adafe9e4atagen2026-10-22 12:30 UTC2138
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2139
    ) -> Result<Option<InviteAvatars>, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2140
        let configuration = self.inner.account().get_media_preview_config_event_content().await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC2141
        match configuration {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2142
            Some(configuration) => Ok(configuration.invite_avatars.map(Into::into)),
7ff8adafe9e4atagen2026-10-22 12:30 UTC2143
            None => Ok(None),
7ff8adafe9e4atagen2026-10-22 12:30 UTC2144
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2145
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2146
7ff8adafe9e4atagen2026-10-22 12:30 UTC2147
    /// Fetch the media preview configuration from the server.
7ff8adafe9e4atagen2026-10-22 12:30 UTC2148
    pub async fn fetch_media_preview_config(
7ff8adafe9e4atagen2026-10-22 12:30 UTC2149
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2150
    ) -> Result<Option<MediaPreviewConfig>, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2151
        Ok(self.inner.account().fetch_media_preview_config_event_content().await?.map(Into::into))
7ff8adafe9e4atagen2026-10-22 12:30 UTC2152
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2153
7ff8adafe9e4atagen2026-10-22 12:30 UTC2154
    /// Gets the `max_upload_size` value from the homeserver, which controls the
7ff8adafe9e4atagen2026-10-22 12:30 UTC2155
    /// max size a media upload request can have.
7ff8adafe9e4atagen2026-10-22 12:30 UTC2156
    pub async fn get_max_media_upload_size(&self) -> Result<u64, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2157
        let max_upload_size = self.inner.load_or_fetch_max_upload_size().await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC2158
        Ok(max_upload_size.into())
7ff8adafe9e4atagen2026-10-22 12:30 UTC2159
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2160
7ff8adafe9e4atagen2026-10-22 12:30 UTC2161
    /// Subscribe to [`RoomInfo`] updates given a provided [`RoomId`].
7ff8adafe9e4atagen2026-10-22 12:30 UTC2162
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC2163
    /// This works even for rooms we haven't received yet, so we can subscribe
7ff8adafe9e4atagen2026-10-22 12:30 UTC2164
    /// to this and wait until we receive updates from them when sync responses
7ff8adafe9e4atagen2026-10-22 12:30 UTC2165
    /// are processed.
7ff8adafe9e4atagen2026-10-22 12:30 UTC2166
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC2167
    /// Note this method should be used sparingly since using callback
7ff8adafe9e4atagen2026-10-22 12:30 UTC2168
    /// interfaces is expensive, as well as keeping them alive for a long
7ff8adafe9e4atagen2026-10-22 12:30 UTC2169
    /// time. Usages of this method should be short-lived and dropped as
7ff8adafe9e4atagen2026-10-22 12:30 UTC2170
    /// soon as possible.
7ff8adafe9e4atagen2026-10-22 12:30 UTC2171
    pub async fn subscribe_to_room_info(
7ff8adafe9e4atagen2026-10-22 12:30 UTC2172
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2173
        room_id: String,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2174
        listener: Box<dyn RoomInfoListener>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2175
    ) -> Result<Arc<TaskHandle>, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2176
        let room_id = RoomId::parse(room_id)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC2177
7ff8adafe9e4atagen2026-10-22 12:30 UTC2178
        // Emit the initial event, if present
7ff8adafe9e4atagen2026-10-22 12:30 UTC2179
        if let Some(room) = self.inner.get_room(&room_id)
7ff8adafe9e4atagen2026-10-22 12:30 UTC2180
            && let Ok(room_info) = RoomInfo::new(&room).await
7ff8adafe9e4atagen2026-10-22 12:30 UTC2181
        {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2182
            listener.call(room_info);
7ff8adafe9e4atagen2026-10-22 12:30 UTC2183
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2184
7ff8adafe9e4atagen2026-10-22 12:30 UTC2185
        Ok(Arc::new(TaskHandle::new(get_runtime_handle().spawn({
7ff8adafe9e4atagen2026-10-22 12:30 UTC2186
            let client = self.inner.clone();
7ff8adafe9e4atagen2026-10-22 12:30 UTC2187
            let mut receiver = client.room_info_notable_update_receiver();
7ff8adafe9e4atagen2026-10-22 12:30 UTC2188
            async move {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2189
                while let Ok(room_update) = receiver.recv().await {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2190
                    if room_update.room_id != room_id {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2191
                        continue;
7ff8adafe9e4atagen2026-10-22 12:30 UTC2192
                    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2193
7ff8adafe9e4atagen2026-10-22 12:30 UTC2194
                    if let Some(room) = client.get_room(&room_id)
7ff8adafe9e4atagen2026-10-22 12:30 UTC2195
                        && let Ok(room_info) = RoomInfo::new(&room).await
7ff8adafe9e4atagen2026-10-22 12:30 UTC2196
                    {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2197
                        listener.call(room_info);
7ff8adafe9e4atagen2026-10-22 12:30 UTC2198
                    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2199
                }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2200
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2201
        }))))
7ff8adafe9e4atagen2026-10-22 12:30 UTC2202
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2203
7ff8adafe9e4atagen2026-10-22 12:30 UTC2204
    /// Whether to enable automatic backpagination under certain conditions
7ff8adafe9e4atagen2026-10-22 12:30 UTC2205
    /// (e.g. when processing read receipts).
7ff8adafe9e4atagen2026-10-22 12:30 UTC2206
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC2207
    /// This is an experimental feature, and might cause performance issues on
7ff8adafe9e4atagen2026-10-22 12:30 UTC2208
    /// large accounts. Use with caution.
7ff8adafe9e4atagen2026-10-22 12:30 UTC2209
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC2210
    /// This must be called after creating a client, but before subscribing to
7ff8adafe9e4atagen2026-10-22 12:30 UTC2211
    /// the event cache (so, before spawning a sync service or a timeline).
7ff8adafe9e4atagen2026-10-22 12:30 UTC2212
    pub fn enable_automatic_backpagination(&self) {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2213
        self.inner.event_cache().config_mut().experimental_auto_backpagination = true;
7ff8adafe9e4atagen2026-10-22 12:30 UTC2214
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2215
7ff8adafe9e4atagen2026-10-22 12:30 UTC2216
    pub fn homeserver_capabilities(&self) -> HomeserverCapabilities {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2217
        HomeserverCapabilities::new(self.inner.homeserver_capabilities())
7ff8adafe9e4atagen2026-10-22 12:30 UTC2218
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2219
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC2220
7ff8adafe9e4atagen2026-10-22 12:30 UTC2221
async fn notification_handler(
7ff8adafe9e4atagen2026-10-22 12:30 UTC2222
    notification: Notification,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2223
    room: matrix_sdk::Room,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2224
    listener: Arc<Box<dyn SyncNotificationListener>>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2225
) {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2226
    let room_id = room.room_id().to_string();
7ff8adafe9e4atagen2026-10-22 12:30 UTC2227
7ff8adafe9e4atagen2026-10-22 12:30 UTC2228
    // Extract information about the actions
7ff8adafe9e4atagen2026-10-22 12:30 UTC2229
    let is_noisy = notification.actions.iter().any(|a| a.sound().is_some());
7ff8adafe9e4atagen2026-10-22 12:30 UTC2230
    let has_mention = notification.actions.iter().any(|a| a.is_highlight());
7ff8adafe9e4atagen2026-10-22 12:30 UTC2231
7ff8adafe9e4atagen2026-10-22 12:30 UTC2232
    // Convert SDK actions to FFI type
7ff8adafe9e4atagen2026-10-22 12:30 UTC2233
    let actions: Vec<crate::notification_settings::Action> =
7ff8adafe9e4atagen2026-10-22 12:30 UTC2234
        notification.actions.into_iter().filter_map(|action| action.try_into().ok()).collect();
7ff8adafe9e4atagen2026-10-22 12:30 UTC2235
7ff8adafe9e4atagen2026-10-22 12:30 UTC2236
    // Convert SDK event to FFI type
7ff8adafe9e4atagen2026-10-22 12:30 UTC2237
    let (sender, event, thread_id, raw_event) = match notification.event {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2238
        RawAnySyncOrStrippedTimelineEvent::Sync(raw) => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2239
            let raw_event = raw.json().get().to_owned();
7ff8adafe9e4atagen2026-10-22 12:30 UTC2240
            match raw.deserialize() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2241
                Ok(deserialized) => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2242
                    let sender = deserialized.sender().to_owned();
7ff8adafe9e4atagen2026-10-22 12:30 UTC2243
                    let thread_id = if let AnySyncTimelineEvent::MessageLike(event) = &deserialized
7ff8adafe9e4atagen2026-10-22 12:30 UTC2244
                        && let Some(AnyMessageLikeEventContent::RoomMessage(content)) =
7ff8adafe9e4atagen2026-10-22 12:30 UTC2245
                            event.original_content()
7ff8adafe9e4atagen2026-10-22 12:30 UTC2246
                        && let Some(Relation::Thread(thread)) = content.relates_to
7ff8adafe9e4atagen2026-10-22 12:30 UTC2247
                    {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2248
                        Some(thread.event_id.to_string())
7ff8adafe9e4atagen2026-10-22 12:30 UTC2249
                    } else {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2250
                        None
7ff8adafe9e4atagen2026-10-22 12:30 UTC2251
                    };
7ff8adafe9e4atagen2026-10-22 12:30 UTC2252
                    let event = NotificationEvent::Timeline {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2253
                        event: Arc::new(crate::event::TimelineEvent(Box::new(deserialized))),
7ff8adafe9e4atagen2026-10-22 12:30 UTC2254
                    };
7ff8adafe9e4atagen2026-10-22 12:30 UTC2255
                    (sender, event, thread_id, raw_event)
7ff8adafe9e4atagen2026-10-22 12:30 UTC2256
                }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2257
                Err(err) => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2258
                    tracing::warn!("Failed to deserialize timeline event: {err}");
7ff8adafe9e4atagen2026-10-22 12:30 UTC2259
                    return;
7ff8adafe9e4atagen2026-10-22 12:30 UTC2260
                }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2261
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2262
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2263
        RawAnySyncOrStrippedTimelineEvent::Stripped(raw) => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2264
            let raw_event = raw.json().get().to_owned();
7ff8adafe9e4atagen2026-10-22 12:30 UTC2265
            match raw.deserialize() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2266
                Ok(deserialized) => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2267
                    let sender = deserialized.sender().to_owned();
7ff8adafe9e4atagen2026-10-22 12:30 UTC2268
                    let event = NotificationEvent::Invite { sender: sender.to_string() };
7ff8adafe9e4atagen2026-10-22 12:30 UTC2269
                    let thread_id = None;
7ff8adafe9e4atagen2026-10-22 12:30 UTC2270
                    (sender, event, thread_id, raw_event)
7ff8adafe9e4atagen2026-10-22 12:30 UTC2271
                }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2272
                Err(err) => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2273
                    tracing::warn!("Failed to deserialize stripped state event: {err}");
7ff8adafe9e4atagen2026-10-22 12:30 UTC2274
                    return;
7ff8adafe9e4atagen2026-10-22 12:30 UTC2275
                }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2276
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2277
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2278
    };
7ff8adafe9e4atagen2026-10-22 12:30 UTC2279
7ff8adafe9e4atagen2026-10-22 12:30 UTC2280
    // Compile sender info
7ff8adafe9e4atagen2026-10-22 12:30 UTC2281
    let sender = room.get_member_no_sync(&sender).await.ok().flatten();
7ff8adafe9e4atagen2026-10-22 12:30 UTC2282
    let sender_info = if let Some(sender) = sender.as_ref() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2283
        NotificationSenderInfo {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2284
            display_name: sender.display_name().map(|name| name.to_owned()),
7ff8adafe9e4atagen2026-10-22 12:30 UTC2285
            avatar_url: sender.avatar_url().map(|uri| uri.to_string()),
7ff8adafe9e4atagen2026-10-22 12:30 UTC2286
            is_name_ambiguous: sender.name_ambiguous(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC2287
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2288
    } else {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2289
        NotificationSenderInfo { display_name: None, avatar_url: None, is_name_ambiguous: false }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2290
    };
7ff8adafe9e4atagen2026-10-22 12:30 UTC2291
7ff8adafe9e4atagen2026-10-22 12:30 UTC2292
    // Compile room info
7ff8adafe9e4atagen2026-10-22 12:30 UTC2293
    let display_name = match room.display_name().await {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2294
        Ok(name) => name.to_string(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC2295
        Err(err) => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2296
            tracing::warn!("Failed to calculate the room's display name: {err}");
7ff8adafe9e4atagen2026-10-22 12:30 UTC2297
            return;
7ff8adafe9e4atagen2026-10-22 12:30 UTC2298
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2299
    };
7ff8adafe9e4atagen2026-10-22 12:30 UTC2300
    let is_direct = match room.is_direct().await {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2301
        Ok(is_direct) => is_direct,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2302
        Err(err) => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2303
            tracing::warn!("Failed to determine if room is direct or not: {err}");
7ff8adafe9e4atagen2026-10-22 12:30 UTC2304
            return;
7ff8adafe9e4atagen2026-10-22 12:30 UTC2305
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2306
    };
7ff8adafe9e4atagen2026-10-22 12:30 UTC2307
    let room_info = NotificationRoomInfo {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2308
        display_name,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2309
        avatar_url: room.avatar_url().map(Into::into),
7ff8adafe9e4atagen2026-10-22 12:30 UTC2310
        canonical_alias: room.canonical_alias().map(Into::into),
7ff8adafe9e4atagen2026-10-22 12:30 UTC2311
        topic: room.topic(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC2312
        join_rule: room.join_rule().map(TryInto::try_into).transpose().ok().flatten(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC2313
        joined_members_count: room.joined_members_count(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC2314
        service_members: room
7ff8adafe9e4atagen2026-10-22 12:30 UTC2315
            .service_members()
7ff8adafe9e4atagen2026-10-22 12:30 UTC2316
            .unwrap_or_default()
7ff8adafe9e4atagen2026-10-22 12:30 UTC2317
            .iter()
7ff8adafe9e4atagen2026-10-22 12:30 UTC2318
            .map(ToString::to_string)
7ff8adafe9e4atagen2026-10-22 12:30 UTC2319
            .collect(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC2320
        active_service_members_count: room
7ff8adafe9e4atagen2026-10-22 12:30 UTC2321
            .update_active_service_members()
7ff8adafe9e4atagen2026-10-22 12:30 UTC2322
            .await
7ff8adafe9e4atagen2026-10-22 12:30 UTC2323
            .ok()
7ff8adafe9e4atagen2026-10-22 12:30 UTC2324
            .flatten()
7ff8adafe9e4atagen2026-10-22 12:30 UTC2325
            .unwrap_or_default()
7ff8adafe9e4atagen2026-10-22 12:30 UTC2326
            .len() as u64,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2327
        is_encrypted: Some(room.encryption_state().is_encrypted()),
7ff8adafe9e4atagen2026-10-22 12:30 UTC2328
        is_direct,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2329
        is_space: room.is_space(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC2330
        is_dm: room.compute_is_dm().await.ok().unwrap_or_default(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC2331
    };
7ff8adafe9e4atagen2026-10-22 12:30 UTC2332
7ff8adafe9e4atagen2026-10-22 12:30 UTC2333
    listener.on_notification(
7ff8adafe9e4atagen2026-10-22 12:30 UTC2334
        NotificationItem {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2335
            event,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2336
            raw_event,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2337
            sender_info,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2338
            room_info,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2339
            is_noisy: Some(is_noisy),
7ff8adafe9e4atagen2026-10-22 12:30 UTC2340
            has_mention: Some(has_mention),
7ff8adafe9e4atagen2026-10-22 12:30 UTC2341
            thread_id,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2342
            actions: Some(actions),
7ff8adafe9e4atagen2026-10-22 12:30 UTC2343
        },
7ff8adafe9e4atagen2026-10-22 12:30 UTC2344
        room_id,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2345
    );
7ff8adafe9e4atagen2026-10-22 12:30 UTC2346
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC2347
7ff8adafe9e4atagen2026-10-22 12:30 UTC2348
#[cfg(feature = "experimental-element-recent-emojis")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC2349
mod recent_emoji {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2350
    use crate::{client::Client, error::ClientError};
7ff8adafe9e4atagen2026-10-22 12:30 UTC2351
7ff8adafe9e4atagen2026-10-22 12:30 UTC2352
    /// Represents an emoji recently used for reactions.
7ff8adafe9e4atagen2026-10-22 12:30 UTC2353
    #[derive(Debug, uniffi::Record)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC2354
    pub struct RecentEmoji {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2355
        /// The actual emoji text representation.
7ff8adafe9e4atagen2026-10-22 12:30 UTC2356
        pub emoji: String,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2357
        /// The number of times this emoji has been used for reactions.
7ff8adafe9e4atagen2026-10-22 12:30 UTC2358
        pub count: u64,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2359
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2360
7ff8adafe9e4atagen2026-10-22 12:30 UTC2361
    #[matrix_sdk_ffi_macros::export]
7ff8adafe9e4atagen2026-10-22 12:30 UTC2362
    impl Client {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2363
        /// Adds a recently used emoji to the list and uploads the updated
7ff8adafe9e4atagen2026-10-22 12:30 UTC2364
        /// `io.element.recent_emoji` content to the global account data.
7ff8adafe9e4atagen2026-10-22 12:30 UTC2365
        pub async fn add_recent_emoji(&self, emoji: String) -> Result<(), ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2366
            Ok(self.inner.account().add_recent_emoji(&emoji).await?)
7ff8adafe9e4atagen2026-10-22 12:30 UTC2367
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2368
7ff8adafe9e4atagen2026-10-22 12:30 UTC2369
        /// Gets the list of recently used emojis from the
7ff8adafe9e4atagen2026-10-22 12:30 UTC2370
        /// `io.element.recent_emoji` global account data.
7ff8adafe9e4atagen2026-10-22 12:30 UTC2371
        pub async fn get_recent_emojis(&self) -> Result<Vec<RecentEmoji>, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2372
            Ok(self
7ff8adafe9e4atagen2026-10-22 12:30 UTC2373
                .inner
7ff8adafe9e4atagen2026-10-22 12:30 UTC2374
                .account()
7ff8adafe9e4atagen2026-10-22 12:30 UTC2375
                .get_recent_emojis(false)
7ff8adafe9e4atagen2026-10-22 12:30 UTC2376
                .await?
7ff8adafe9e4atagen2026-10-22 12:30 UTC2377
                .into_iter()
7ff8adafe9e4atagen2026-10-22 12:30 UTC2378
                .map(|(emoji, count)| RecentEmoji { emoji, count: count.into() })
7ff8adafe9e4atagen2026-10-22 12:30 UTC2379
                .collect::<Vec<RecentEmoji>>())
7ff8adafe9e4atagen2026-10-22 12:30 UTC2380
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2381
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2382
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC2383
7ff8adafe9e4atagen2026-10-22 12:30 UTC2384
#[matrix_sdk_ffi_macros::export(callback_interface)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC2385
pub trait MediaPreviewConfigListener: SyncOutsideWasm + SendOutsideWasm {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2386
    fn on_change(&self, media_preview_config: Option<MediaPreviewConfig>);
7ff8adafe9e4atagen2026-10-22 12:30 UTC2387
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC2388
7ff8adafe9e4atagen2026-10-22 12:30 UTC2389
#[matrix_sdk_ffi_macros::export(callback_interface)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC2390
pub trait IgnoredUsersListener: SyncOutsideWasm + SendOutsideWasm {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2391
    fn call(&self, ignored_user_ids: Vec<String>);
7ff8adafe9e4atagen2026-10-22 12:30 UTC2392
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC2393
7ff8adafe9e4atagen2026-10-22 12:30 UTC2394
#[derive(uniffi::Enum)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC2395
pub enum NotificationProcessSetup {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2396
    MultipleProcesses,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2397
    SingleProcess { sync_service: Arc<SyncService> },
7ff8adafe9e4atagen2026-10-22 12:30 UTC2398
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC2399
7ff8adafe9e4atagen2026-10-22 12:30 UTC2400
impl From<NotificationProcessSetup> for MatrixNotificationProcessSetup {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2401
    fn from(value: NotificationProcessSetup) -> Self {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2402
        match value {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2403
            NotificationProcessSetup::MultipleProcesses => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2404
                MatrixNotificationProcessSetup::MultipleProcesses
7ff8adafe9e4atagen2026-10-22 12:30 UTC2405
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2406
            NotificationProcessSetup::SingleProcess { sync_service } => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2407
                MatrixNotificationProcessSetup::SingleProcess {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2408
                    sync_service: sync_service.inner.clone(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC2409
                }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2410
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2411
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2412
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2413
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC2414
7ff8adafe9e4atagen2026-10-22 12:30 UTC2415
/// Information about a room, that was resolved from a room alias.
7ff8adafe9e4atagen2026-10-22 12:30 UTC2416
#[derive(uniffi::Record)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC2417
pub struct ResolvedRoomAlias {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2418
    /// The room ID that the alias resolved to.
7ff8adafe9e4atagen2026-10-22 12:30 UTC2419
    pub room_id: String,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2420
    /// A list of servers that can be used to find the room by its room ID.
7ff8adafe9e4atagen2026-10-22 12:30 UTC2421
    pub servers: Vec<String>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2422
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC2423
7ff8adafe9e4atagen2026-10-22 12:30 UTC2424
impl From<get_alias::v3::Response> for ResolvedRoomAlias {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2425
    fn from(value: get_alias::v3::Response) -> Self {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2426
        Self {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2427
            room_id: value.room_id.to_string(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC2428
            servers: value.servers.iter().map(ToString::to_string).collect(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC2429
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2430
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2431
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC2432
7ff8adafe9e4atagen2026-10-22 12:30 UTC2433
#[derive(uniffi::Record)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC2434
pub struct SearchUsersResults {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2435
    pub results: Vec<UserProfile>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2436
    pub limited: bool,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2437
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC2438
7ff8adafe9e4atagen2026-10-22 12:30 UTC2439
impl From<search_users::v3::Response> for SearchUsersResults {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2440
    fn from(value: search_users::v3::Response) -> Self {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2441
        let results: Vec<UserProfile> = value.results.iter().map(UserProfile::from).collect();
7ff8adafe9e4atagen2026-10-22 12:30 UTC2442
        SearchUsersResults { results, limited: value.limited }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2443
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2444
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC2445
7ff8adafe9e4atagen2026-10-22 12:30 UTC2446
#[derive(uniffi::Record)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC2447
pub struct UserProfile {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2448
    pub user_id: String,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2449
    pub display_name: Option<String>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2450
    pub avatar_url: Option<String>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2451
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC2452
7ff8adafe9e4atagen2026-10-22 12:30 UTC2453
impl UserProfile {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2454
    /// Fetch the profile for the given user ID, using the given [`Account`]
7ff8adafe9e4atagen2026-10-22 12:30 UTC2455
    /// API.
7ff8adafe9e4atagen2026-10-22 12:30 UTC2456
    pub(crate) async fn fetch(account: &Account, user_id: &UserId) -> Result<Self, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2457
        let response = account.fetch_user_profile_of(user_id).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC2458
        let display_name = response.get_static::<DisplayName>()?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC2459
        let avatar_url = response.get_static::<AvatarUrl>()?.map(|url| url.to_string());
7ff8adafe9e4atagen2026-10-22 12:30 UTC2460
7ff8adafe9e4atagen2026-10-22 12:30 UTC2461
        Ok(UserProfile { user_id: user_id.to_string(), display_name, avatar_url })
7ff8adafe9e4atagen2026-10-22 12:30 UTC2462
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2463
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC2464
7ff8adafe9e4atagen2026-10-22 12:30 UTC2465
impl From<&search_users::v3::User> for UserProfile {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2466
    fn from(value: &search_users::v3::User) -> Self {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2467
        UserProfile {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2468
            user_id: value.user_id.to_string(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC2469
            display_name: value.display_name.clone(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC2470
            avatar_url: value.avatar_url.as_ref().map(|url| url.to_string()),
7ff8adafe9e4atagen2026-10-22 12:30 UTC2471
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2472
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2473
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC2474
7ff8adafe9e4atagen2026-10-22 12:30 UTC2475
impl Client {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2476
    fn process_session_change(&self, session_change: SessionChange) {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2477
        if let Some(delegate_data) = self.delegate_data.get() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2478
            debug!("Applying session change: {session_change:?}");
7ff8adafe9e4atagen2026-10-22 12:30 UTC2479
            let delegate = delegate_data.delegate.clone();
7ff8adafe9e4atagen2026-10-22 12:30 UTC2480
            get_runtime_handle().spawn_blocking(move || match session_change {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2481
                SessionChange::UnknownToken(unknown_token) => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2482
                    delegate.did_receive_auth_error(unknown_token.soft_logout);
7ff8adafe9e4atagen2026-10-22 12:30 UTC2483
                }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2484
                SessionChange::TokensRefreshed => {}
7ff8adafe9e4atagen2026-10-22 12:30 UTC2485
            });
7ff8adafe9e4atagen2026-10-22 12:30 UTC2486
        } else {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2487
            debug!(
7ff8adafe9e4atagen2026-10-22 12:30 UTC2488
                "No client delegate found, session change couldn't be applied: {session_change:?}"
7ff8adafe9e4atagen2026-10-22 12:30 UTC2489
            );
7ff8adafe9e4atagen2026-10-22 12:30 UTC2490
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2491
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2492
7ff8adafe9e4atagen2026-10-22 12:30 UTC2493
    fn retrieve_session(
7ff8adafe9e4atagen2026-10-22 12:30 UTC2494
        session_delegate: Arc<dyn ClientSessionDelegate>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2495
        user_id: &UserId,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2496
    ) -> anyhow::Result<SessionTokens> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2497
        Ok(session_delegate.retrieve_session_from_keychain(user_id.to_string())?.into_tokens())
7ff8adafe9e4atagen2026-10-22 12:30 UTC2498
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2499
7ff8adafe9e4atagen2026-10-22 12:30 UTC2500
    fn session_inner(client: matrix_sdk::Client) -> Result<Session, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2501
        let auth_api = client.auth_api().context("Missing authentication API")?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC2502
7ff8adafe9e4atagen2026-10-22 12:30 UTC2503
        let homeserver_url = client.homeserver().into();
7ff8adafe9e4atagen2026-10-22 12:30 UTC2504
        let sliding_sync_version = client.sliding_sync_version();
7ff8adafe9e4atagen2026-10-22 12:30 UTC2505
7ff8adafe9e4atagen2026-10-22 12:30 UTC2506
        Session::new(auth_api, homeserver_url, sliding_sync_version.into())
7ff8adafe9e4atagen2026-10-22 12:30 UTC2507
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2508
7ff8adafe9e4atagen2026-10-22 12:30 UTC2509
    fn save_session(
7ff8adafe9e4atagen2026-10-22 12:30 UTC2510
        session_delegate: Arc<dyn ClientSessionDelegate>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2511
        client: matrix_sdk::Client,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2512
    ) -> anyhow::Result<()> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2513
        let session = Self::session_inner(client)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC2514
        session_delegate.save_session_in_keychain(session);
7ff8adafe9e4atagen2026-10-22 12:30 UTC2515
        Ok(())
7ff8adafe9e4atagen2026-10-22 12:30 UTC2516
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2517
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC2518
7ff8adafe9e4atagen2026-10-22 12:30 UTC2519
/// Configure how many rooms will be restored when restoring the session with
7ff8adafe9e4atagen2026-10-22 12:30 UTC2520
/// [`Client::restore_session_with`].
7ff8adafe9e4atagen2026-10-22 12:30 UTC2521
///
7ff8adafe9e4atagen2026-10-22 12:30 UTC2522
/// Please, see the documentation of [`matrix_sdk::store::RoomLoadSettings`] to
7ff8adafe9e4atagen2026-10-22 12:30 UTC2523
/// learn more.
7ff8adafe9e4atagen2026-10-22 12:30 UTC2524
#[derive(uniffi::Enum)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC2525
pub enum RoomLoadSettings {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2526
    /// Load all rooms from the `StateStore` into the in-memory state store
7ff8adafe9e4atagen2026-10-22 12:30 UTC2527
    /// `BaseStateStore`.
7ff8adafe9e4atagen2026-10-22 12:30 UTC2528
    All,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2529
7ff8adafe9e4atagen2026-10-22 12:30 UTC2530
    /// Load a single room from the `StateStore` into the in-memory state
7ff8adafe9e4atagen2026-10-22 12:30 UTC2531
    /// store `BaseStateStore`.
7ff8adafe9e4atagen2026-10-22 12:30 UTC2532
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC2533
    /// Please, be careful with this option. Read the documentation of
7ff8adafe9e4atagen2026-10-22 12:30 UTC2534
    /// [`RoomLoadSettings`].
7ff8adafe9e4atagen2026-10-22 12:30 UTC2535
    One { room_id: String },
7ff8adafe9e4atagen2026-10-22 12:30 UTC2536
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC2537
7ff8adafe9e4atagen2026-10-22 12:30 UTC2538
impl TryInto<SdkRoomLoadSettings> for RoomLoadSettings {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2539
    type Error = String;
7ff8adafe9e4atagen2026-10-22 12:30 UTC2540
7ff8adafe9e4atagen2026-10-22 12:30 UTC2541
    fn try_into(self) -> Result<SdkRoomLoadSettings, Self::Error> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2542
        Ok(match self {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2543
            Self::All => SdkRoomLoadSettings::All,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2544
            Self::One { room_id } => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2545
                SdkRoomLoadSettings::One(RoomId::parse(room_id).map_err(|error| error.to_string())?)
7ff8adafe9e4atagen2026-10-22 12:30 UTC2546
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2547
        })
7ff8adafe9e4atagen2026-10-22 12:30 UTC2548
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2549
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC2550
7ff8adafe9e4atagen2026-10-22 12:30 UTC2551
#[derive(uniffi::Record)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC2552
pub struct NotificationPowerLevels {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2553
    pub room: i32,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2554
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC2555
7ff8adafe9e4atagen2026-10-22 12:30 UTC2556
impl From<NotificationPowerLevels> for ruma::power_levels::NotificationPowerLevels {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2557
    fn from(value: NotificationPowerLevels) -> Self {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2558
        let mut notification_power_levels = Self::new();
7ff8adafe9e4atagen2026-10-22 12:30 UTC2559
        notification_power_levels.room = value.room.into();
7ff8adafe9e4atagen2026-10-22 12:30 UTC2560
        notification_power_levels
7ff8adafe9e4atagen2026-10-22 12:30 UTC2561
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2562
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC2563
7ff8adafe9e4atagen2026-10-22 12:30 UTC2564
#[derive(uniffi::Record)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC2565
pub struct PowerLevels {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2566
    pub users_default: Option<i32>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2567
    pub events_default: Option<i32>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2568
    pub state_default: Option<i32>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2569
    pub ban: Option<i32>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2570
    pub kick: Option<i32>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2571
    pub redact: Option<i32>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2572
    pub invite: Option<i32>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2573
    pub notifications: Option<NotificationPowerLevels>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2574
    pub users: HashMap<String, i32>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2575
    pub events: HashMap<String, i32>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2576
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC2577
7ff8adafe9e4atagen2026-10-22 12:30 UTC2578
impl From<PowerLevels> for RoomPowerLevelsContentOverride {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2579
    fn from(value: PowerLevels) -> Self {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2580
        let mut power_levels = RoomPowerLevelsContentOverride::default();
7ff8adafe9e4atagen2026-10-22 12:30 UTC2581
        power_levels.users_default = value.users_default.map(Into::into);
7ff8adafe9e4atagen2026-10-22 12:30 UTC2582
        power_levels.state_default = value.state_default.map(Into::into);
7ff8adafe9e4atagen2026-10-22 12:30 UTC2583
        power_levels.events_default = value.events_default.map(Into::into);
7ff8adafe9e4atagen2026-10-22 12:30 UTC2584
        power_levels.ban = value.ban.map(Into::into);
7ff8adafe9e4atagen2026-10-22 12:30 UTC2585
        power_levels.kick = value.kick.map(Into::into);
7ff8adafe9e4atagen2026-10-22 12:30 UTC2586
        power_levels.redact = value.redact.map(Into::into);
7ff8adafe9e4atagen2026-10-22 12:30 UTC2587
        power_levels.invite = value.invite.map(Into::into);
7ff8adafe9e4atagen2026-10-22 12:30 UTC2588
        power_levels.notifications = value.notifications.map(Into::into).unwrap_or_default();
7ff8adafe9e4atagen2026-10-22 12:30 UTC2589
        power_levels.users = value
7ff8adafe9e4atagen2026-10-22 12:30 UTC2590
            .users
7ff8adafe9e4atagen2026-10-22 12:30 UTC2591
            .iter()
7ff8adafe9e4atagen2026-10-22 12:30 UTC2592
            .filter_map(|(user_id, power_level)| match UserId::parse(user_id) {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2593
                Ok(id) => Some((id, (*power_level).into())),
7ff8adafe9e4atagen2026-10-22 12:30 UTC2594
                Err(e) => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2595
                    error!(user_id, "Skipping invalid user ID, error: {e}");
7ff8adafe9e4atagen2026-10-22 12:30 UTC2596
                    None
7ff8adafe9e4atagen2026-10-22 12:30 UTC2597
                }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2598
            })
7ff8adafe9e4atagen2026-10-22 12:30 UTC2599
            .collect();
7ff8adafe9e4atagen2026-10-22 12:30 UTC2600
        power_levels.events = value
7ff8adafe9e4atagen2026-10-22 12:30 UTC2601
            .events
7ff8adafe9e4atagen2026-10-22 12:30 UTC2602
            .iter()
7ff8adafe9e4atagen2026-10-22 12:30 UTC2603
            .map(|(event_type, power_level)| {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2604
                let event_type: ruma::events::TimelineEventType = event_type.as_str().into();
7ff8adafe9e4atagen2026-10-22 12:30 UTC2605
                (event_type, (*power_level).into())
7ff8adafe9e4atagen2026-10-22 12:30 UTC2606
            })
7ff8adafe9e4atagen2026-10-22 12:30 UTC2607
            .collect();
7ff8adafe9e4atagen2026-10-22 12:30 UTC2608
        power_levels
7ff8adafe9e4atagen2026-10-22 12:30 UTC2609
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2610
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC2611
7ff8adafe9e4atagen2026-10-22 12:30 UTC2612
#[derive(uniffi::Record)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC2613
pub struct CreateRoomParameters {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2614
    pub name: Option<String>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2615
    #[uniffi(default = None)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC2616
    pub topic: Option<String>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2617
    pub is_encrypted: bool,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2618
    #[uniffi(default = false)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC2619
    pub is_direct: bool,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2620
    pub visibility: RoomVisibility,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2621
    pub preset: RoomPreset,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2622
    #[uniffi(default = None)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC2623
    pub invite: Option<Vec<String>>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2624
    #[uniffi(default = None)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC2625
    pub avatar: Option<String>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2626
    #[uniffi(default = None)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC2627
    pub power_level_content_override: Option<PowerLevels>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2628
    #[uniffi(default = None)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC2629
    pub join_rule_override: Option<JoinRule>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2630
    #[uniffi(default = None)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC2631
    pub history_visibility_override: Option<RoomHistoryVisibility>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2632
    #[uniffi(default = None)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC2633
    pub canonical_alias: Option<String>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2634
    #[uniffi(default = false)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC2635
    pub is_space: bool,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2636
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC2637
7ff8adafe9e4atagen2026-10-22 12:30 UTC2638
impl TryFrom<CreateRoomParameters> for create_room::v3::Request {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2639
    type Error = ClientError;
7ff8adafe9e4atagen2026-10-22 12:30 UTC2640
7ff8adafe9e4atagen2026-10-22 12:30 UTC2641
    fn try_from(value: CreateRoomParameters) -> Result<create_room::v3::Request, Self::Error> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2642
        let mut request = create_room::v3::Request::new();
7ff8adafe9e4atagen2026-10-22 12:30 UTC2643
        request.name = value.name;
7ff8adafe9e4atagen2026-10-22 12:30 UTC2644
        request.topic = value.topic;
7ff8adafe9e4atagen2026-10-22 12:30 UTC2645
        request.is_direct = value.is_direct;
7ff8adafe9e4atagen2026-10-22 12:30 UTC2646
        request.visibility = value.visibility.into();
7ff8adafe9e4atagen2026-10-22 12:30 UTC2647
        request.preset = Some(value.preset.into());
7ff8adafe9e4atagen2026-10-22 12:30 UTC2648
        request.room_alias_name = value.canonical_alias;
7ff8adafe9e4atagen2026-10-22 12:30 UTC2649
        request.invite = match value.invite {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2650
            Some(invite) => invite
7ff8adafe9e4atagen2026-10-22 12:30 UTC2651
                .iter()
7ff8adafe9e4atagen2026-10-22 12:30 UTC2652
                .filter_map(|user_id| match UserId::parse(user_id) {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2653
                    Ok(id) => Some(id),
7ff8adafe9e4atagen2026-10-22 12:30 UTC2654
                    Err(e) => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2655
                        error!(user_id, "Skipping invalid user ID, error: {e}");
7ff8adafe9e4atagen2026-10-22 12:30 UTC2656
                        None
7ff8adafe9e4atagen2026-10-22 12:30 UTC2657
                    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2658
                })
7ff8adafe9e4atagen2026-10-22 12:30 UTC2659
                .collect(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC2660
            None => vec![],
7ff8adafe9e4atagen2026-10-22 12:30 UTC2661
        };
7ff8adafe9e4atagen2026-10-22 12:30 UTC2662
7ff8adafe9e4atagen2026-10-22 12:30 UTC2663
        let mut initial_state: Vec<Raw<AnyInitialStateEvent>> = vec![];
7ff8adafe9e4atagen2026-10-22 12:30 UTC2664
7ff8adafe9e4atagen2026-10-22 12:30 UTC2665
        if value.is_encrypted {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2666
            let content =
7ff8adafe9e4atagen2026-10-22 12:30 UTC2667
                RoomEncryptionEventContent::new(EventEncryptionAlgorithm::MegolmV1AesSha2);
7ff8adafe9e4atagen2026-10-22 12:30 UTC2668
            initial_state.push(InitialStateEvent::with_empty_state_key(content).to_raw_any());
7ff8adafe9e4atagen2026-10-22 12:30 UTC2669
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2670
7ff8adafe9e4atagen2026-10-22 12:30 UTC2671
        if let Some(url) = value.avatar {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2672
            let mut content = RoomAvatarEventContent::new();
7ff8adafe9e4atagen2026-10-22 12:30 UTC2673
            content.url = Some(url.into());
7ff8adafe9e4atagen2026-10-22 12:30 UTC2674
            initial_state.push(InitialStateEvent::with_empty_state_key(content).to_raw_any());
7ff8adafe9e4atagen2026-10-22 12:30 UTC2675
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2676
7ff8adafe9e4atagen2026-10-22 12:30 UTC2677
        if let Some(join_rule_override) = value.join_rule_override {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2678
            let content = RoomJoinRulesEventContent::new(join_rule_override.try_into()?);
7ff8adafe9e4atagen2026-10-22 12:30 UTC2679
            initial_state.push(InitialStateEvent::with_empty_state_key(content).to_raw_any());
7ff8adafe9e4atagen2026-10-22 12:30 UTC2680
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2681
7ff8adafe9e4atagen2026-10-22 12:30 UTC2682
        if let Some(history_visibility_override) = value.history_visibility_override {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2683
            let content =
7ff8adafe9e4atagen2026-10-22 12:30 UTC2684
                RoomHistoryVisibilityEventContent::new(history_visibility_override.try_into()?);
7ff8adafe9e4atagen2026-10-22 12:30 UTC2685
            initial_state.push(InitialStateEvent::with_empty_state_key(content).to_raw_any());
7ff8adafe9e4atagen2026-10-22 12:30 UTC2686
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2687
7ff8adafe9e4atagen2026-10-22 12:30 UTC2688
        request.initial_state = initial_state;
7ff8adafe9e4atagen2026-10-22 12:30 UTC2689
7ff8adafe9e4atagen2026-10-22 12:30 UTC2690
        if value.is_space {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2691
            let mut creation_content = CreationContent::new();
7ff8adafe9e4atagen2026-10-22 12:30 UTC2692
            creation_content.room_type = Some(RoomType::Space);
7ff8adafe9e4atagen2026-10-22 12:30 UTC2693
            request.creation_content = Some(Raw::new(&creation_content)?);
7ff8adafe9e4atagen2026-10-22 12:30 UTC2694
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2695
7ff8adafe9e4atagen2026-10-22 12:30 UTC2696
        if let Some(power_levels) = value.power_level_content_override {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2697
            match Raw::<RoomPowerLevelsContentOverride>::new(&power_levels.into()) {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2698
                Ok(power_levels) => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2699
                    request.power_level_content_override = Some(power_levels);
7ff8adafe9e4atagen2026-10-22 12:30 UTC2700
                }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2701
                Err(e) => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2702
                    return Err(ClientError::Generic {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2703
                        msg: format!("Failed to serialize power levels, error: {e}"),
7ff8adafe9e4atagen2026-10-22 12:30 UTC2704
                        details: Some(format!("{e:?}")),
7ff8adafe9e4atagen2026-10-22 12:30 UTC2705
                    });
7ff8adafe9e4atagen2026-10-22 12:30 UTC2706
                }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2707
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2708
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2709
7ff8adafe9e4atagen2026-10-22 12:30 UTC2710
        Ok(request)
7ff8adafe9e4atagen2026-10-22 12:30 UTC2711
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2712
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC2713
7ff8adafe9e4atagen2026-10-22 12:30 UTC2714
#[derive(uniffi::Enum)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC2715
pub enum RoomVisibility {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2716
    /// Indicates that the room will be shown in the published room list.
7ff8adafe9e4atagen2026-10-22 12:30 UTC2717
    Public,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2718
7ff8adafe9e4atagen2026-10-22 12:30 UTC2719
    /// Indicates that the room will not be shown in the published room list.
7ff8adafe9e4atagen2026-10-22 12:30 UTC2720
    Private,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2721
7ff8adafe9e4atagen2026-10-22 12:30 UTC2722
    /// A custom value that's not present in the spec.
7ff8adafe9e4atagen2026-10-22 12:30 UTC2723
    Custom { value: String },
7ff8adafe9e4atagen2026-10-22 12:30 UTC2724
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC2725
7ff8adafe9e4atagen2026-10-22 12:30 UTC2726
impl From<RoomVisibility> for Visibility {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2727
    fn from(value: RoomVisibility) -> Self {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2728
        match value {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2729
            RoomVisibility::Public => Self::Public,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2730
            RoomVisibility::Private => Self::Private,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2731
            RoomVisibility::Custom { value } => value.as_str().into(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC2732
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2733
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2734
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC2735
7ff8adafe9e4atagen2026-10-22 12:30 UTC2736
impl From<Visibility> for RoomVisibility {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2737
    fn from(value: Visibility) -> Self {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2738
        match value {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2739
            Visibility::Public => Self::Public,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2740
            Visibility::Private => Self::Private,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2741
            _ => Self::Custom { value: value.as_str().to_owned() },
7ff8adafe9e4atagen2026-10-22 12:30 UTC2742
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2743
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2744
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC2745
7ff8adafe9e4atagen2026-10-22 12:30 UTC2746
#[derive(uniffi::Enum)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC2747
#[allow(clippy::enum_variant_names)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC2748
pub enum RoomPreset {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2749
    /// `join_rules` is set to `invite` and `history_visibility` is set to
7ff8adafe9e4atagen2026-10-22 12:30 UTC2750
    /// `shared`.
7ff8adafe9e4atagen2026-10-22 12:30 UTC2751
    PrivateChat,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2752
7ff8adafe9e4atagen2026-10-22 12:30 UTC2753
    /// `join_rules` is set to `public` and `history_visibility` is set to
7ff8adafe9e4atagen2026-10-22 12:30 UTC2754
    /// `shared`.
7ff8adafe9e4atagen2026-10-22 12:30 UTC2755
    PublicChat,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2756
7ff8adafe9e4atagen2026-10-22 12:30 UTC2757
    /// Same as `PrivateChat`, but all initial invitees get the same power level
7ff8adafe9e4atagen2026-10-22 12:30 UTC2758
    /// as the creator.
7ff8adafe9e4atagen2026-10-22 12:30 UTC2759
    TrustedPrivateChat,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2760
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC2761
7ff8adafe9e4atagen2026-10-22 12:30 UTC2762
impl From<RoomPreset> for create_room::v3::RoomPreset {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2763
    fn from(value: RoomPreset) -> Self {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2764
        match value {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2765
            RoomPreset::PrivateChat => Self::PrivateChat,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2766
            RoomPreset::PublicChat => Self::PublicChat,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2767
            RoomPreset::TrustedPrivateChat => Self::TrustedPrivateChat,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2768
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2769
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2770
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC2771
7ff8adafe9e4atagen2026-10-22 12:30 UTC2772
#[derive(uniffi::Record)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC2773
pub struct Session {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2774
    // Same fields as the Session type in matrix-sdk, just simpler types
7ff8adafe9e4atagen2026-10-22 12:30 UTC2775
    /// The access token used for this session.
7ff8adafe9e4atagen2026-10-22 12:30 UTC2776
    pub access_token: String,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2777
    /// The token used for [refreshing the access token], if any.
7ff8adafe9e4atagen2026-10-22 12:30 UTC2778
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC2779
    /// [refreshing the access token]: https://spec.matrix.org/v1.3/client-server-api/#refreshing-access-tokens
7ff8adafe9e4atagen2026-10-22 12:30 UTC2780
    pub refresh_token: Option<String>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2781
    /// The user the access token was issued for.
7ff8adafe9e4atagen2026-10-22 12:30 UTC2782
    pub user_id: String,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2783
    /// The ID of the client device.
7ff8adafe9e4atagen2026-10-22 12:30 UTC2784
    pub device_id: String,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2785
7ff8adafe9e4atagen2026-10-22 12:30 UTC2786
    // FFI-only fields (for now)
7ff8adafe9e4atagen2026-10-22 12:30 UTC2787
    /// The URL for the homeserver used for this session.
7ff8adafe9e4atagen2026-10-22 12:30 UTC2788
    pub homeserver_url: String,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2789
    /// Additional data for this session if OpenID Connect was used for
7ff8adafe9e4atagen2026-10-22 12:30 UTC2790
    /// authentication.
7ff8adafe9e4atagen2026-10-22 12:30 UTC2791
    pub oauth_data: Option<String>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2792
    /// The sliding sync version used for this session.
7ff8adafe9e4atagen2026-10-22 12:30 UTC2793
    pub sliding_sync_version: SlidingSyncVersion,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2794
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC2795
7ff8adafe9e4atagen2026-10-22 12:30 UTC2796
impl Session {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2797
    fn new(
7ff8adafe9e4atagen2026-10-22 12:30 UTC2798
        auth_api: AuthApi,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2799
        homeserver_url: String,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2800
        sliding_sync_version: SlidingSyncVersion,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2801
    ) -> Result<Session, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2802
        match auth_api {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2803
            // Build the session from the regular Matrix Auth Session.
7ff8adafe9e4atagen2026-10-22 12:30 UTC2804
            AuthApi::Matrix(a) => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2805
                let matrix_sdk::authentication::matrix::MatrixSession {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2806
                    meta: matrix_sdk::SessionMeta { user_id, device_id },
7ff8adafe9e4atagen2026-10-22 12:30 UTC2807
                    tokens: matrix_sdk::SessionTokens { access_token, refresh_token },
7ff8adafe9e4atagen2026-10-22 12:30 UTC2808
                } = a.session().context("Missing session")?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC2809
7ff8adafe9e4atagen2026-10-22 12:30 UTC2810
                Ok(Session {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2811
                    access_token,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2812
                    refresh_token,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2813
                    user_id: user_id.to_string(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC2814
                    device_id: device_id.to_string(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC2815
                    homeserver_url,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2816
                    oauth_data: None,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2817
                    sliding_sync_version,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2818
                })
7ff8adafe9e4atagen2026-10-22 12:30 UTC2819
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2820
            // Build the session from the OAuth UserSession.
7ff8adafe9e4atagen2026-10-22 12:30 UTC2821
            AuthApi::OAuth(api) => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2822
                let matrix_sdk::authentication::oauth::UserSession {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2823
                    meta: matrix_sdk::SessionMeta { user_id, device_id },
7ff8adafe9e4atagen2026-10-22 12:30 UTC2824
                    tokens: matrix_sdk::SessionTokens { access_token, refresh_token },
7ff8adafe9e4atagen2026-10-22 12:30 UTC2825
                } = api.user_session().context("Missing session")?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC2826
                let client_id = api.client_id().context("OAuth client ID is missing.")?.clone();
7ff8adafe9e4atagen2026-10-22 12:30 UTC2827
                let oauth_data = OAuthSessionData { client_id };
7ff8adafe9e4atagen2026-10-22 12:30 UTC2828
7ff8adafe9e4atagen2026-10-22 12:30 UTC2829
                let oauth_data_string = serde_json::to_string(&oauth_data).ok();
7ff8adafe9e4atagen2026-10-22 12:30 UTC2830
                Ok(Session {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2831
                    access_token,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2832
                    refresh_token,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2833
                    user_id: user_id.to_string(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC2834
                    device_id: device_id.to_string(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC2835
                    homeserver_url,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2836
                    oauth_data: oauth_data_string,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2837
                    sliding_sync_version,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2838
                })
7ff8adafe9e4atagen2026-10-22 12:30 UTC2839
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2840
            _ => Err(anyhow!("Unknown authentication API").into()),
7ff8adafe9e4atagen2026-10-22 12:30 UTC2841
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2842
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2843
7ff8adafe9e4atagen2026-10-22 12:30 UTC2844
    fn into_tokens(self) -> matrix_sdk::SessionTokens {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2845
        SessionTokens { access_token: self.access_token, refresh_token: self.refresh_token }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2846
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2847
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC2848
7ff8adafe9e4atagen2026-10-22 12:30 UTC2849
impl TryFrom<Session> for AuthSession {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2850
    type Error = ClientError;
7ff8adafe9e4atagen2026-10-22 12:30 UTC2851
    fn try_from(value: Session) -> Result<Self, Self::Error> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2852
        let Session {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2853
            access_token,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2854
            refresh_token,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2855
            user_id,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2856
            device_id,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2857
            homeserver_url: _,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2858
            oauth_data: oauth_data_string,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2859
            sliding_sync_version: _,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2860
        } = value;
7ff8adafe9e4atagen2026-10-22 12:30 UTC2861
7ff8adafe9e4atagen2026-10-22 12:30 UTC2862
        if let Some(oauth_data_string) = oauth_data_string {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2863
            // Create an OAuth Session.
7ff8adafe9e4atagen2026-10-22 12:30 UTC2864
            let oauth_data = serde_json::from_str::<OAuthSessionData>(&oauth_data_string)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC2865
7ff8adafe9e4atagen2026-10-22 12:30 UTC2866
            let user_session = matrix_sdk::authentication::oauth::UserSession {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2867
                meta: matrix_sdk::SessionMeta {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2868
                    user_id: user_id.try_into()?,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2869
                    device_id: device_id.into(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC2870
                },
7ff8adafe9e4atagen2026-10-22 12:30 UTC2871
                tokens: matrix_sdk::SessionTokens { access_token, refresh_token },
7ff8adafe9e4atagen2026-10-22 12:30 UTC2872
            };
7ff8adafe9e4atagen2026-10-22 12:30 UTC2873
7ff8adafe9e4atagen2026-10-22 12:30 UTC2874
            let session = OAuthSession { client_id: oauth_data.client_id, user: user_session };
7ff8adafe9e4atagen2026-10-22 12:30 UTC2875
7ff8adafe9e4atagen2026-10-22 12:30 UTC2876
            Ok(AuthSession::OAuth(session.into()))
7ff8adafe9e4atagen2026-10-22 12:30 UTC2877
        } else {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2878
            // Create a regular Matrix Session.
7ff8adafe9e4atagen2026-10-22 12:30 UTC2879
            let session = matrix_sdk::authentication::matrix::MatrixSession {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2880
                meta: matrix_sdk::SessionMeta {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2881
                    user_id: user_id.try_into()?,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2882
                    device_id: device_id.into(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC2883
                },
7ff8adafe9e4atagen2026-10-22 12:30 UTC2884
                tokens: matrix_sdk::SessionTokens { access_token, refresh_token },
7ff8adafe9e4atagen2026-10-22 12:30 UTC2885
            };
7ff8adafe9e4atagen2026-10-22 12:30 UTC2886
7ff8adafe9e4atagen2026-10-22 12:30 UTC2887
            Ok(AuthSession::Matrix(session))
7ff8adafe9e4atagen2026-10-22 12:30 UTC2888
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2889
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2890
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC2891
7ff8adafe9e4atagen2026-10-22 12:30 UTC2892
/// Represents a client registration against an OAuth authentication issuer.
7ff8adafe9e4atagen2026-10-22 12:30 UTC2893
#[derive(Serialize, Deserialize)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC2894
pub(crate) struct OAuthSessionData {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2895
    client_id: ClientId,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2896
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC2897
7ff8adafe9e4atagen2026-10-22 12:30 UTC2898
#[derive(uniffi::Enum)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC2899
pub enum AccountManagementAction {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2900
    Profile,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2901
    DevicesList,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2902
    DeviceView { device_id: String },
7ff8adafe9e4atagen2026-10-22 12:30 UTC2903
    DeviceDelete { device_id: String },
7ff8adafe9e4atagen2026-10-22 12:30 UTC2904
    AccountDeactivate,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2905
    CrossSigningReset,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2906
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC2907
7ff8adafe9e4atagen2026-10-22 12:30 UTC2908
impl<'a> From<&'a AccountManagementAction> for AccountManagementActionData<'a> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2909
    fn from(value: &'a AccountManagementAction) -> Self {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2910
        match value {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2911
            AccountManagementAction::Profile => Self::Profile,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2912
            AccountManagementAction::DevicesList => Self::DevicesList,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2913
            AccountManagementAction::DeviceView { device_id } => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2914
                Self::DeviceView(DeviceViewData::new(device_id.as_str().into()))
7ff8adafe9e4atagen2026-10-22 12:30 UTC2915
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2916
            AccountManagementAction::DeviceDelete { device_id } => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2917
                Self::DeviceDelete(DeviceDeleteData::new(device_id.as_str().into()))
7ff8adafe9e4atagen2026-10-22 12:30 UTC2918
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2919
            AccountManagementAction::AccountDeactivate => Self::AccountDeactivate,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2920
            AccountManagementAction::CrossSigningReset => Self::CrossSigningReset,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2921
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2922
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2923
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC2924
7ff8adafe9e4atagen2026-10-22 12:30 UTC2925
#[matrix_sdk_ffi_macros::export]
7ff8adafe9e4atagen2026-10-22 12:30 UTC2926
fn gen_transaction_id() -> String {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2927
    TransactionId::new().to_string()
7ff8adafe9e4atagen2026-10-22 12:30 UTC2928
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC2929
7ff8adafe9e4atagen2026-10-22 12:30 UTC2930
/// A file handle that takes ownership of a media file on disk. When the handle
7ff8adafe9e4atagen2026-10-22 12:30 UTC2931
/// is dropped, the file will be removed from the disk.
7ff8adafe9e4atagen2026-10-22 12:30 UTC2932
#[derive(uniffi::Object)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC2933
pub struct MediaFileHandle {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2934
    #[cfg(not(target_family = "wasm"))]
7ff8adafe9e4atagen2026-10-22 12:30 UTC2935
    inner: std::sync::RwLock<Option<SdkMediaFileHandle>>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2936
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC2937
7ff8adafe9e4atagen2026-10-22 12:30 UTC2938
impl MediaFileHandle {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2939
    #[cfg(not(target_family = "wasm"))]
7ff8adafe9e4atagen2026-10-22 12:30 UTC2940
    fn new(handle: SdkMediaFileHandle) -> Self {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2941
        Self { inner: std::sync::RwLock::new(Some(handle)) }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2942
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2943
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC2944
7ff8adafe9e4atagen2026-10-22 12:30 UTC2945
#[matrix_sdk_ffi_macros::export]
7ff8adafe9e4atagen2026-10-22 12:30 UTC2946
impl MediaFileHandle {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2947
    /// Get the media file's path.
7ff8adafe9e4atagen2026-10-22 12:30 UTC2948
    pub fn path(&self) -> Result<String, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2949
        #[cfg(not(target_family = "wasm"))]
7ff8adafe9e4atagen2026-10-22 12:30 UTC2950
        return Ok(self
7ff8adafe9e4atagen2026-10-22 12:30 UTC2951
            .inner
7ff8adafe9e4atagen2026-10-22 12:30 UTC2952
            .read()
7ff8adafe9e4atagen2026-10-22 12:30 UTC2953
            .unwrap()
7ff8adafe9e4atagen2026-10-22 12:30 UTC2954
            .as_ref()
7ff8adafe9e4atagen2026-10-22 12:30 UTC2955
            .context("MediaFileHandle must not be used after calling persist")?
7ff8adafe9e4atagen2026-10-22 12:30 UTC2956
            .path()
7ff8adafe9e4atagen2026-10-22 12:30 UTC2957
            .to_str()
7ff8adafe9e4atagen2026-10-22 12:30 UTC2958
            .unwrap()
7ff8adafe9e4atagen2026-10-22 12:30 UTC2959
            .to_owned());
7ff8adafe9e4atagen2026-10-22 12:30 UTC2960
        #[cfg(target_family = "wasm")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC2961
        Err(ClientError::Generic {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2962
            msg: "MediaFileHandle.path() is not supported on WASM targets".to_string(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC2963
            details: None,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2964
        })
7ff8adafe9e4atagen2026-10-22 12:30 UTC2965
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2966
7ff8adafe9e4atagen2026-10-22 12:30 UTC2967
    pub fn persist(&self, path: String) -> Result<bool, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2968
        #[cfg(not(target_family = "wasm"))]
7ff8adafe9e4atagen2026-10-22 12:30 UTC2969
        {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2970
            let mut guard = self.inner.write().unwrap();
7ff8adafe9e4atagen2026-10-22 12:30 UTC2971
            Ok(
7ff8adafe9e4atagen2026-10-22 12:30 UTC2972
                match guard
7ff8adafe9e4atagen2026-10-22 12:30 UTC2973
                    .take()
7ff8adafe9e4atagen2026-10-22 12:30 UTC2974
                    .context("MediaFileHandle was already persisted")?
7ff8adafe9e4atagen2026-10-22 12:30 UTC2975
                    .persist(path.as_ref())
7ff8adafe9e4atagen2026-10-22 12:30 UTC2976
                {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2977
                    Ok(_) => true,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2978
                    Err(e) => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2979
                        *guard = Some(e.file);
7ff8adafe9e4atagen2026-10-22 12:30 UTC2980
                        false
7ff8adafe9e4atagen2026-10-22 12:30 UTC2981
                    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2982
                },
7ff8adafe9e4atagen2026-10-22 12:30 UTC2983
            )
7ff8adafe9e4atagen2026-10-22 12:30 UTC2984
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2985
        #[cfg(target_family = "wasm")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC2986
        Err(ClientError::Generic {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2987
            msg: "MediaFileHandle.persist() is not supported on WASM targets".to_string(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC2988
            details: None,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2989
        })
7ff8adafe9e4atagen2026-10-22 12:30 UTC2990
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC2991
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC2992
7ff8adafe9e4atagen2026-10-22 12:30 UTC2993
#[derive(Clone, uniffi::Enum)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC2994
pub enum SlidingSyncVersion {
7ff8adafe9e4atagen2026-10-22 12:30 UTC2995
    None,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2996
    Native,
7ff8adafe9e4atagen2026-10-22 12:30 UTC2997
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC2998
7ff8adafe9e4atagen2026-10-22 12:30 UTC2999
impl From<SdkSlidingSyncVersion> for SlidingSyncVersion {
7ff8adafe9e4atagen2026-10-22 12:30 UTC3000
    fn from(value: SdkSlidingSyncVersion) -> Self {
7ff8adafe9e4atagen2026-10-22 12:30 UTC3001
        match value {
7ff8adafe9e4atagen2026-10-22 12:30 UTC3002
            SdkSlidingSyncVersion::None => Self::None,
7ff8adafe9e4atagen2026-10-22 12:30 UTC3003
            SdkSlidingSyncVersion::Native => Self::Native,
7ff8adafe9e4atagen2026-10-22 12:30 UTC3004
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC3005
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC3006
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC3007
7ff8adafe9e4atagen2026-10-22 12:30 UTC3008
impl TryFrom<SlidingSyncVersion> for SdkSlidingSyncVersion {
7ff8adafe9e4atagen2026-10-22 12:30 UTC3009
    type Error = ClientError;
7ff8adafe9e4atagen2026-10-22 12:30 UTC3010
7ff8adafe9e4atagen2026-10-22 12:30 UTC3011
    fn try_from(value: SlidingSyncVersion) -> Result<Self, Self::Error> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC3012
        Ok(match value {
7ff8adafe9e4atagen2026-10-22 12:30 UTC3013
            SlidingSyncVersion::None => Self::None,
7ff8adafe9e4atagen2026-10-22 12:30 UTC3014
            SlidingSyncVersion::Native => Self::Native,
7ff8adafe9e4atagen2026-10-22 12:30 UTC3015
        })
7ff8adafe9e4atagen2026-10-22 12:30 UTC3016
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC3017
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC3018
7ff8adafe9e4atagen2026-10-22 12:30 UTC3019
#[derive(Clone, uniffi::Enum)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC3020
pub enum OAuthPrompt {
7ff8adafe9e4atagen2026-10-22 12:30 UTC3021
    /// The Authorization Server should prompt the End-User to create a user
7ff8adafe9e4atagen2026-10-22 12:30 UTC3022
    /// account.
7ff8adafe9e4atagen2026-10-22 12:30 UTC3023
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC3024
    /// Defined in [Initiating User Registration via OpenID Connect](https://openid.net/specs/openid-connect-prompt-create-1_0.html).
7ff8adafe9e4atagen2026-10-22 12:30 UTC3025
    Create,
7ff8adafe9e4atagen2026-10-22 12:30 UTC3026
7ff8adafe9e4atagen2026-10-22 12:30 UTC3027
    /// The Authorization Server should prompt the End-User for
7ff8adafe9e4atagen2026-10-22 12:30 UTC3028
    /// reauthentication.
7ff8adafe9e4atagen2026-10-22 12:30 UTC3029
    Login,
7ff8adafe9e4atagen2026-10-22 12:30 UTC3030
7ff8adafe9e4atagen2026-10-22 12:30 UTC3031
    /// The Authorization Server should prompt the End-User for consent before
7ff8adafe9e4atagen2026-10-22 12:30 UTC3032
    /// returning information to the Client.
7ff8adafe9e4atagen2026-10-22 12:30 UTC3033
    Consent,
7ff8adafe9e4atagen2026-10-22 12:30 UTC3034
7ff8adafe9e4atagen2026-10-22 12:30 UTC3035
    /// An unknown value.
7ff8adafe9e4atagen2026-10-22 12:30 UTC3036
    Unknown { value: String },
7ff8adafe9e4atagen2026-10-22 12:30 UTC3037
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC3038
7ff8adafe9e4atagen2026-10-22 12:30 UTC3039
impl From<RumaOAuthPrompt> for OAuthPrompt {
7ff8adafe9e4atagen2026-10-22 12:30 UTC3040
    fn from(value: RumaOAuthPrompt) -> Self {
7ff8adafe9e4atagen2026-10-22 12:30 UTC3041
        match value {
7ff8adafe9e4atagen2026-10-22 12:30 UTC3042
            RumaOAuthPrompt::Create => Self::Create,
7ff8adafe9e4atagen2026-10-22 12:30 UTC3043
            value => match value.as_str() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC3044
                "consent" => Self::Consent,
7ff8adafe9e4atagen2026-10-22 12:30 UTC3045
                "login" => Self::Login,
7ff8adafe9e4atagen2026-10-22 12:30 UTC3046
                _ => Self::Unknown { value: value.to_string() },
7ff8adafe9e4atagen2026-10-22 12:30 UTC3047
            },
7ff8adafe9e4atagen2026-10-22 12:30 UTC3048
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC3049
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC3050
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC3051
7ff8adafe9e4atagen2026-10-22 12:30 UTC3052
impl From<OAuthPrompt> for RumaOAuthPrompt {
7ff8adafe9e4atagen2026-10-22 12:30 UTC3053
    fn from(value: OAuthPrompt) -> Self {
7ff8adafe9e4atagen2026-10-22 12:30 UTC3054
        match value {
7ff8adafe9e4atagen2026-10-22 12:30 UTC3055
            OAuthPrompt::Create => Self::Create,
7ff8adafe9e4atagen2026-10-22 12:30 UTC3056
            OAuthPrompt::Consent => Self::from("consent"),
7ff8adafe9e4atagen2026-10-22 12:30 UTC3057
            OAuthPrompt::Login => Self::from("login"),
7ff8adafe9e4atagen2026-10-22 12:30 UTC3058
            OAuthPrompt::Unknown { value } => value.into(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC3059
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC3060
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC3061
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC3062
7ff8adafe9e4atagen2026-10-22 12:30 UTC3063
/// The rule used for users wishing to join this room.
7ff8adafe9e4atagen2026-10-22 12:30 UTC3064
#[derive(Debug, Clone, uniffi::Enum)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC3065
pub enum JoinRule {
7ff8adafe9e4atagen2026-10-22 12:30 UTC3066
    /// Anyone can join the room without any prior action.
7ff8adafe9e4atagen2026-10-22 12:30 UTC3067
    Public,
7ff8adafe9e4atagen2026-10-22 12:30 UTC3068
7ff8adafe9e4atagen2026-10-22 12:30 UTC3069
    /// A user who wishes to join the room must first receive an invite to the
7ff8adafe9e4atagen2026-10-22 12:30 UTC3070
    /// room from someone already inside of the room.
7ff8adafe9e4atagen2026-10-22 12:30 UTC3071
    Invite,
7ff8adafe9e4atagen2026-10-22 12:30 UTC3072
7ff8adafe9e4atagen2026-10-22 12:30 UTC3073
    /// Users can join the room if they are invited, or they can request an
7ff8adafe9e4atagen2026-10-22 12:30 UTC3074
    /// invite to the room.
7ff8adafe9e4atagen2026-10-22 12:30 UTC3075
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC3076
    /// They can be allowed (invited) or denied (kicked/banned) access.
7ff8adafe9e4atagen2026-10-22 12:30 UTC3077
    Knock,
7ff8adafe9e4atagen2026-10-22 12:30 UTC3078
7ff8adafe9e4atagen2026-10-22 12:30 UTC3079
    /// Reserved but not yet implemented by the Matrix specification.
7ff8adafe9e4atagen2026-10-22 12:30 UTC3080
    Private,
7ff8adafe9e4atagen2026-10-22 12:30 UTC3081
7ff8adafe9e4atagen2026-10-22 12:30 UTC3082
    /// Users can join the room if they are invited, or if they meet any of the
7ff8adafe9e4atagen2026-10-22 12:30 UTC3083
    /// conditions described in a set of [`AllowRule`]s.
7ff8adafe9e4atagen2026-10-22 12:30 UTC3084
    Restricted { rules: Vec<AllowRule> },
7ff8adafe9e4atagen2026-10-22 12:30 UTC3085
7ff8adafe9e4atagen2026-10-22 12:30 UTC3086
    /// Users can join the room if they are invited, or if they meet any of the
7ff8adafe9e4atagen2026-10-22 12:30 UTC3087
    /// conditions described in a set of [`AllowRule`]s, or they can request
7ff8adafe9e4atagen2026-10-22 12:30 UTC3088
    /// an invite to the room.
7ff8adafe9e4atagen2026-10-22 12:30 UTC3089
    KnockRestricted { rules: Vec<AllowRule> },
7ff8adafe9e4atagen2026-10-22 12:30 UTC3090
7ff8adafe9e4atagen2026-10-22 12:30 UTC3091
    /// A custom join rule, up for interpretation by the consumer.
7ff8adafe9e4atagen2026-10-22 12:30 UTC3092
    Custom {
7ff8adafe9e4atagen2026-10-22 12:30 UTC3093
        /// The string representation for this custom rule.
7ff8adafe9e4atagen2026-10-22 12:30 UTC3094
        repr: String,
7ff8adafe9e4atagen2026-10-22 12:30 UTC3095
    },
7ff8adafe9e4atagen2026-10-22 12:30 UTC3096
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC3097
7ff8adafe9e4atagen2026-10-22 12:30 UTC3098
/// An allow rule which defines a condition that allows joining a room.
7ff8adafe9e4atagen2026-10-22 12:30 UTC3099
#[derive(Debug, Clone, uniffi::Enum)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC3100
pub enum AllowRule {
7ff8adafe9e4atagen2026-10-22 12:30 UTC3101
    /// Only a member of the `room_id` Room can join the one this rule is used
7ff8adafe9e4atagen2026-10-22 12:30 UTC3102
    /// in.
7ff8adafe9e4atagen2026-10-22 12:30 UTC3103
    RoomMembership { room_id: String },
7ff8adafe9e4atagen2026-10-22 12:30 UTC3104
7ff8adafe9e4atagen2026-10-22 12:30 UTC3105
    /// A custom allow rule implementation, containing its JSON representation
7ff8adafe9e4atagen2026-10-22 12:30 UTC3106
    /// as a `String`.
7ff8adafe9e4atagen2026-10-22 12:30 UTC3107
    Custom { json: String },
7ff8adafe9e4atagen2026-10-22 12:30 UTC3108
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC3109
7ff8adafe9e4atagen2026-10-22 12:30 UTC3110
impl TryFrom<JoinRule> for RumaJoinRule {
7ff8adafe9e4atagen2026-10-22 12:30 UTC3111
    type Error = ClientError;
7ff8adafe9e4atagen2026-10-22 12:30 UTC3112
7ff8adafe9e4atagen2026-10-22 12:30 UTC3113
    fn try_from(value: JoinRule) -> Result<Self, Self::Error> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC3114
        match value {
7ff8adafe9e4atagen2026-10-22 12:30 UTC3115
            JoinRule::Public => Ok(Self::Public),
7ff8adafe9e4atagen2026-10-22 12:30 UTC3116
            JoinRule::Invite => Ok(Self::Invite),
7ff8adafe9e4atagen2026-10-22 12:30 UTC3117
            JoinRule::Knock => Ok(Self::Knock),
7ff8adafe9e4atagen2026-10-22 12:30 UTC3118
            JoinRule::Private => Ok(Self::Private),
7ff8adafe9e4atagen2026-10-22 12:30 UTC3119
            JoinRule::Restricted { rules } => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC3120
                let rules = ruma_allow_rules_from_ffi(rules)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC3121
                Ok(Self::Restricted(ruma::events::room::join_rules::Restricted::new(rules)))
7ff8adafe9e4atagen2026-10-22 12:30 UTC3122
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC3123
            JoinRule::KnockRestricted { rules } => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC3124
                let rules = ruma_allow_rules_from_ffi(rules)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC3125
                Ok(Self::KnockRestricted(ruma::events::room::join_rules::Restricted::new(rules)))
7ff8adafe9e4atagen2026-10-22 12:30 UTC3126
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC3127
            JoinRule::Custom { repr } => Ok(serde_json::from_str(&repr)?),
7ff8adafe9e4atagen2026-10-22 12:30 UTC3128
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC3129
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC3130
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC3131
7ff8adafe9e4atagen2026-10-22 12:30 UTC3132
fn ruma_allow_rules_from_ffi(value: Vec<AllowRule>) -> Result<Vec<RumaAllowRule>, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC3133
    let mut ret = Vec::with_capacity(value.len());
7ff8adafe9e4atagen2026-10-22 12:30 UTC3134
    for rule in value {
7ff8adafe9e4atagen2026-10-22 12:30 UTC3135
        let rule: Result<RumaAllowRule, ClientError> = rule.try_into();
7ff8adafe9e4atagen2026-10-22 12:30 UTC3136
        match rule {
7ff8adafe9e4atagen2026-10-22 12:30 UTC3137
            Ok(rule) => ret.push(rule),
7ff8adafe9e4atagen2026-10-22 12:30 UTC3138
            Err(error) => return Err(error),
7ff8adafe9e4atagen2026-10-22 12:30 UTC3139
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC3140
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC3141
    Ok(ret)
7ff8adafe9e4atagen2026-10-22 12:30 UTC3142
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC3143
7ff8adafe9e4atagen2026-10-22 12:30 UTC3144
impl TryFrom<AllowRule> for RumaAllowRule {
7ff8adafe9e4atagen2026-10-22 12:30 UTC3145
    type Error = ClientError;
7ff8adafe9e4atagen2026-10-22 12:30 UTC3146
7ff8adafe9e4atagen2026-10-22 12:30 UTC3147
    fn try_from(value: AllowRule) -> Result<Self, Self::Error> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC3148
        match value {
7ff8adafe9e4atagen2026-10-22 12:30 UTC3149
            AllowRule::RoomMembership { room_id } => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC3150
                let room_id = RoomId::parse(room_id)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC3151
                Ok(Self::RoomMembership(ruma::room::RoomMembership::new(room_id)))
7ff8adafe9e4atagen2026-10-22 12:30 UTC3152
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC3153
            AllowRule::Custom { json } => Ok(serde_json::from_str(&json)?),
7ff8adafe9e4atagen2026-10-22 12:30 UTC3154
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC3155
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC3156
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC3157
7ff8adafe9e4atagen2026-10-22 12:30 UTC3158
impl TryFrom<RumaJoinRule> for JoinRule {
7ff8adafe9e4atagen2026-10-22 12:30 UTC3159
    type Error = String;
7ff8adafe9e4atagen2026-10-22 12:30 UTC3160
    fn try_from(value: RumaJoinRule) -> Result<Self, Self::Error> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC3161
        match value {
7ff8adafe9e4atagen2026-10-22 12:30 UTC3162
            RumaJoinRule::Knock => Ok(JoinRule::Knock),
7ff8adafe9e4atagen2026-10-22 12:30 UTC3163
            RumaJoinRule::Public => Ok(JoinRule::Public),
7ff8adafe9e4atagen2026-10-22 12:30 UTC3164
            RumaJoinRule::Private => Ok(JoinRule::Private),
7ff8adafe9e4atagen2026-10-22 12:30 UTC3165
            RumaJoinRule::KnockRestricted(restricted) => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC3166
                let rules = restricted.allow.into_iter().map(TryInto::try_into).collect::<Result<
7ff8adafe9e4atagen2026-10-22 12:30 UTC3167
                    Vec<_>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC3168
                    Self::Error,
7ff8adafe9e4atagen2026-10-22 12:30 UTC3169
                >>(
7ff8adafe9e4atagen2026-10-22 12:30 UTC3170
                )?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC3171
                Ok(JoinRule::KnockRestricted { rules })
7ff8adafe9e4atagen2026-10-22 12:30 UTC3172
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC3173
            RumaJoinRule::Restricted(restricted) => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC3174
                let rules = restricted.allow.into_iter().map(TryInto::try_into).collect::<Result<
7ff8adafe9e4atagen2026-10-22 12:30 UTC3175
                    Vec<_>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC3176
                    Self::Error,
7ff8adafe9e4atagen2026-10-22 12:30 UTC3177
                >>(
7ff8adafe9e4atagen2026-10-22 12:30 UTC3178
                )?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC3179
                Ok(JoinRule::Restricted { rules })
7ff8adafe9e4atagen2026-10-22 12:30 UTC3180
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC3181
            RumaJoinRule::Invite => Ok(JoinRule::Invite),
7ff8adafe9e4atagen2026-10-22 12:30 UTC3182
            RumaJoinRule::_Custom(_) => Ok(JoinRule::Custom { repr: value.as_str().to_owned() }),
7ff8adafe9e4atagen2026-10-22 12:30 UTC3183
            _ => Err(format!("Unknown JoinRule: {value:?}")),
7ff8adafe9e4atagen2026-10-22 12:30 UTC3184
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC3185
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC3186
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC3187
7ff8adafe9e4atagen2026-10-22 12:30 UTC3188
impl TryFrom<RumaAllowRule> for AllowRule {
7ff8adafe9e4atagen2026-10-22 12:30 UTC3189
    type Error = String;
7ff8adafe9e4atagen2026-10-22 12:30 UTC3190
    fn try_from(value: RumaAllowRule) -> Result<Self, Self::Error> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC3191
        match value {
7ff8adafe9e4atagen2026-10-22 12:30 UTC3192
            RumaAllowRule::RoomMembership(membership) => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC3193
                Ok(AllowRule::RoomMembership { room_id: membership.room_id.to_string() })
7ff8adafe9e4atagen2026-10-22 12:30 UTC3194
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC3195
            RumaAllowRule::_Custom(repr) => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC3196
                let json = serde_json::to_string(&repr)
7ff8adafe9e4atagen2026-10-22 12:30 UTC3197
                    .map_err(|e| format!("Couldn't serialize custom AllowRule: {e:?}"))?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC3198
                Ok(Self::Custom { json })
7ff8adafe9e4atagen2026-10-22 12:30 UTC3199
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC3200
            _ => Err(format!("Invalid AllowRule: {value:?}")),
7ff8adafe9e4atagen2026-10-22 12:30 UTC3201
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC3202
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC3203
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC3204
7ff8adafe9e4atagen2026-10-22 12:30 UTC3205
/// Contains the disk size of the different stores, if known. It won't be
7ff8adafe9e4atagen2026-10-22 12:30 UTC3206
/// available for in-memory stores.
7ff8adafe9e4atagen2026-10-22 12:30 UTC3207
#[derive(Debug, Clone, uniffi::Record)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC3208
pub struct StoreSizes {
7ff8adafe9e4atagen2026-10-22 12:30 UTC3209
    /// The size of the CryptoStore.
7ff8adafe9e4atagen2026-10-22 12:30 UTC3210
    crypto_store: Option<u64>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC3211
    /// The size of the StateStore.
7ff8adafe9e4atagen2026-10-22 12:30 UTC3212
    state_store: Option<u64>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC3213
    /// The size of the EventCacheStore.
7ff8adafe9e4atagen2026-10-22 12:30 UTC3214
    event_cache_store: Option<u64>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC3215
    /// The size of the MediaStore.
7ff8adafe9e4atagen2026-10-22 12:30 UTC3216
    media_store: Option<u64>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC3217
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC3218
7ff8adafe9e4atagen2026-10-22 12:30 UTC3219
impl From<matrix_sdk::StoreSizes> for StoreSizes {
7ff8adafe9e4atagen2026-10-22 12:30 UTC3220
    fn from(value: matrix_sdk::StoreSizes) -> Self {
7ff8adafe9e4atagen2026-10-22 12:30 UTC3221
        Self {
7ff8adafe9e4atagen2026-10-22 12:30 UTC3222
            crypto_store: value.crypto_store.map(|v| v as u64),
7ff8adafe9e4atagen2026-10-22 12:30 UTC3223
            state_store: value.state_store.map(|v| v as u64),
7ff8adafe9e4atagen2026-10-22 12:30 UTC3224
            event_cache_store: value.event_cache_store.map(|v| v as u64),
7ff8adafe9e4atagen2026-10-22 12:30 UTC3225
            media_store: value.media_store.map(|v| v as u64),
7ff8adafe9e4atagen2026-10-22 12:30 UTC3226
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC3227
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC3228
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC3229
7ff8adafe9e4atagen2026-10-22 12:30 UTC3230
#[derive(uniffi::Object)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC3231
pub struct HomeserverCapabilities {
7ff8adafe9e4atagen2026-10-22 12:30 UTC3232
    inner: matrix_sdk::HomeserverCapabilities,
7ff8adafe9e4atagen2026-10-22 12:30 UTC3233
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC3234
7ff8adafe9e4atagen2026-10-22 12:30 UTC3235
impl HomeserverCapabilities {
7ff8adafe9e4atagen2026-10-22 12:30 UTC3236
    pub(crate) fn new(capabilities: matrix_sdk::HomeserverCapabilities) -> Self {
7ff8adafe9e4atagen2026-10-22 12:30 UTC3237
        Self { inner: capabilities }
7ff8adafe9e4atagen2026-10-22 12:30 UTC3238
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC3239
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC3240
7ff8adafe9e4atagen2026-10-22 12:30 UTC3241
#[matrix_sdk_ffi_macros::export]
7ff8adafe9e4atagen2026-10-22 12:30 UTC3242
impl HomeserverCapabilities {
7ff8adafe9e4atagen2026-10-22 12:30 UTC3243
    pub async fn refresh(&self) -> Result<(), ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC3244
        Ok(self.inner.refresh().await?)
7ff8adafe9e4atagen2026-10-22 12:30 UTC3245
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC3246
7ff8adafe9e4atagen2026-10-22 12:30 UTC3247
    pub async fn can_change_password(&self) -> Result<bool, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC3248
        Ok(self.inner.can_change_password().await?)
7ff8adafe9e4atagen2026-10-22 12:30 UTC3249
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC3250
7ff8adafe9e4atagen2026-10-22 12:30 UTC3251
    pub async fn can_change_displayname(&self) -> Result<bool, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC3252
        Ok(self.inner.can_change_displayname().await?)
7ff8adafe9e4atagen2026-10-22 12:30 UTC3253
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC3254
7ff8adafe9e4atagen2026-10-22 12:30 UTC3255
    pub async fn can_change_avatar(&self) -> Result<bool, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC3256
        Ok(self.inner.can_change_avatar().await?)
7ff8adafe9e4atagen2026-10-22 12:30 UTC3257
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC3258
7ff8adafe9e4atagen2026-10-22 12:30 UTC3259
    pub async fn can_change_thirdparty_ids(&self) -> Result<bool, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC3260
        Ok(self.inner.can_change_thirdparty_ids().await?)
7ff8adafe9e4atagen2026-10-22 12:30 UTC3261
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC3262
7ff8adafe9e4atagen2026-10-22 12:30 UTC3263
    pub async fn can_get_login_token(&self) -> Result<bool, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC3264
        Ok(self.inner.can_get_login_token().await?)
7ff8adafe9e4atagen2026-10-22 12:30 UTC3265
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC3266
7ff8adafe9e4atagen2026-10-22 12:30 UTC3267
    pub async fn extended_profile_fields(&self) -> Result<ExtendedProfileFields, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC3268
        let profile_fields = self.inner.extended_profile_fields().await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC3269
        Ok(ExtendedProfileFields {
7ff8adafe9e4atagen2026-10-22 12:30 UTC3270
            enabled: profile_fields.enabled,
7ff8adafe9e4atagen2026-10-22 12:30 UTC3271
            allowed: profile_fields
7ff8adafe9e4atagen2026-10-22 12:30 UTC3272
                .allowed
7ff8adafe9e4atagen2026-10-22 12:30 UTC3273
                .unwrap_or_default()
7ff8adafe9e4atagen2026-10-22 12:30 UTC3274
                .iter()
7ff8adafe9e4atagen2026-10-22 12:30 UTC3275
                .map(ToString::to_string)
7ff8adafe9e4atagen2026-10-22 12:30 UTC3276
                .collect(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC3277
            disallowed: profile_fields
7ff8adafe9e4atagen2026-10-22 12:30 UTC3278
                .disallowed
7ff8adafe9e4atagen2026-10-22 12:30 UTC3279
                .unwrap_or_default()
7ff8adafe9e4atagen2026-10-22 12:30 UTC3280
                .iter()
7ff8adafe9e4atagen2026-10-22 12:30 UTC3281
                .map(ToString::to_string)
7ff8adafe9e4atagen2026-10-22 12:30 UTC3282
                .collect(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC3283
        })
7ff8adafe9e4atagen2026-10-22 12:30 UTC3284
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC3285
7ff8adafe9e4atagen2026-10-22 12:30 UTC3286
    pub async fn forgets_room_when_leaving(&self) -> Result<bool, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC3287
        Ok(self.inner.forgets_room_when_leaving().await?)
7ff8adafe9e4atagen2026-10-22 12:30 UTC3288
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC3289
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC3290
7ff8adafe9e4atagen2026-10-22 12:30 UTC3291
#[derive(uniffi::Record)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC3292
pub struct ExtendedProfileFields {
7ff8adafe9e4atagen2026-10-22 12:30 UTC3293
    pub enabled: bool,
7ff8adafe9e4atagen2026-10-22 12:30 UTC3294
    pub allowed: Vec<String>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC3295
    pub disallowed: Vec<String>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC3296
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC3297
7ff8adafe9e4atagen2026-10-22 12:30 UTC3298
#[cfg(test)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC3299
mod tests {
7ff8adafe9e4atagen2026-10-22 12:30 UTC3300
    use std::time::Duration;
7ff8adafe9e4atagen2026-10-22 12:30 UTC3301
7ff8adafe9e4atagen2026-10-22 12:30 UTC3302
    use ruma::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC3303
        ServerName,
7ff8adafe9e4atagen2026-10-22 12:30 UTC3304
        api::client::room::{Visibility, create_room},
7ff8adafe9e4atagen2026-10-22 12:30 UTC3305
        authentication::TokenType,
7ff8adafe9e4atagen2026-10-22 12:30 UTC3306
        events::StateEventType,
7ff8adafe9e4atagen2026-10-22 12:30 UTC3307
        room::RoomType,
7ff8adafe9e4atagen2026-10-22 12:30 UTC3308
    };
7ff8adafe9e4atagen2026-10-22 12:30 UTC3309
7ff8adafe9e4atagen2026-10-22 12:30 UTC3310
    use crate::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC3311
        client::{CreateRoomParameters, JoinRule, OpenIdToken, RoomPreset, RoomVisibility},
7ff8adafe9e4atagen2026-10-22 12:30 UTC3312
        room::RoomHistoryVisibility,
7ff8adafe9e4atagen2026-10-22 12:30 UTC3313
    };
7ff8adafe9e4atagen2026-10-22 12:30 UTC3314
7ff8adafe9e4atagen2026-10-22 12:30 UTC3315
    #[test]
7ff8adafe9e4atagen2026-10-22 12:30 UTC3316
    fn test_create_room_parameters_mapping() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC3317
        let params = CreateRoomParameters {
7ff8adafe9e4atagen2026-10-22 12:30 UTC3318
            name: Some("A room".to_owned()),
7ff8adafe9e4atagen2026-10-22 12:30 UTC3319
            topic: Some("A topic".to_owned()),
7ff8adafe9e4atagen2026-10-22 12:30 UTC3320
            is_encrypted: true,
7ff8adafe9e4atagen2026-10-22 12:30 UTC3321
            is_direct: true,
7ff8adafe9e4atagen2026-10-22 12:30 UTC3322
            visibility: RoomVisibility::Public,
7ff8adafe9e4atagen2026-10-22 12:30 UTC3323
            preset: RoomPreset::PublicChat,
7ff8adafe9e4atagen2026-10-22 12:30 UTC3324
            invite: Some(vec!["@user:example.com".to_owned()]),
7ff8adafe9e4atagen2026-10-22 12:30 UTC3325
            avatar: Some("http://example.com/avatar.jpg".to_owned()),
7ff8adafe9e4atagen2026-10-22 12:30 UTC3326
            power_level_content_override: None,
7ff8adafe9e4atagen2026-10-22 12:30 UTC3327
            join_rule_override: Some(JoinRule::Knock),
7ff8adafe9e4atagen2026-10-22 12:30 UTC3328
            history_visibility_override: Some(RoomHistoryVisibility::Shared),
7ff8adafe9e4atagen2026-10-22 12:30 UTC3329
            canonical_alias: Some("#a-room:example.com".to_owned()),
7ff8adafe9e4atagen2026-10-22 12:30 UTC3330
            is_space: true,
7ff8adafe9e4atagen2026-10-22 12:30 UTC3331
        };
7ff8adafe9e4atagen2026-10-22 12:30 UTC3332
7ff8adafe9e4atagen2026-10-22 12:30 UTC3333
        let request: create_room::v3::Request =
7ff8adafe9e4atagen2026-10-22 12:30 UTC3334
            params.try_into().expect("CreateRoomParameters couldn't be transformed into a Request");
7ff8adafe9e4atagen2026-10-22 12:30 UTC3335
        let initial_state = request
7ff8adafe9e4atagen2026-10-22 12:30 UTC3336
            .initial_state
7ff8adafe9e4atagen2026-10-22 12:30 UTC3337
            .iter()
7ff8adafe9e4atagen2026-10-22 12:30 UTC3338
            .map(|raw| raw.deserialize().expect("Initial state event failed to deserialize"))
7ff8adafe9e4atagen2026-10-22 12:30 UTC3339
            .collect::<Vec<_>>();
7ff8adafe9e4atagen2026-10-22 12:30 UTC3340
7ff8adafe9e4atagen2026-10-22 12:30 UTC3341
        assert_eq!(request.name, Some("A room".to_owned()));
7ff8adafe9e4atagen2026-10-22 12:30 UTC3342
        assert_eq!(request.topic, Some("A topic".to_owned()));
7ff8adafe9e4atagen2026-10-22 12:30 UTC3343
        assert!(initial_state.iter().any(|e| e.event_type() == StateEventType::RoomEncryption));
7ff8adafe9e4atagen2026-10-22 12:30 UTC3344
        assert!(request.is_direct);
7ff8adafe9e4atagen2026-10-22 12:30 UTC3345
        assert_eq!(request.visibility, Visibility::Public);
7ff8adafe9e4atagen2026-10-22 12:30 UTC3346
        assert_eq!(request.preset, Some(create_room::v3::RoomPreset::PublicChat));
7ff8adafe9e4atagen2026-10-22 12:30 UTC3347
        assert_eq!(request.invite.len(), 1);
7ff8adafe9e4atagen2026-10-22 12:30 UTC3348
        assert!(initial_state.iter().any(|e| e.event_type() == StateEventType::RoomAvatar));
7ff8adafe9e4atagen2026-10-22 12:30 UTC3349
        assert!(initial_state.iter().any(|e| e.event_type() == StateEventType::RoomJoinRules));
7ff8adafe9e4atagen2026-10-22 12:30 UTC3350
        assert!(
7ff8adafe9e4atagen2026-10-22 12:30 UTC3351
            initial_state.iter().any(|e| e.event_type() == StateEventType::RoomHistoryVisibility)
7ff8adafe9e4atagen2026-10-22 12:30 UTC3352
        );
7ff8adafe9e4atagen2026-10-22 12:30 UTC3353
        assert_eq!(request.room_alias_name, Some("#a-room:example.com".to_owned()));
7ff8adafe9e4atagen2026-10-22 12:30 UTC3354
7ff8adafe9e4atagen2026-10-22 12:30 UTC3355
        let room_type = request
7ff8adafe9e4atagen2026-10-22 12:30 UTC3356
            .creation_content
7ff8adafe9e4atagen2026-10-22 12:30 UTC3357
            .expect("Creation content is missing")
7ff8adafe9e4atagen2026-10-22 12:30 UTC3358
            .deserialize()
7ff8adafe9e4atagen2026-10-22 12:30 UTC3359
            .expect("Creation content can't be deserialized")
7ff8adafe9e4atagen2026-10-22 12:30 UTC3360
            .room_type;
7ff8adafe9e4atagen2026-10-22 12:30 UTC3361
        assert_eq!(room_type, Some(RoomType::Space));
7ff8adafe9e4atagen2026-10-22 12:30 UTC3362
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC3363
7ff8adafe9e4atagen2026-10-22 12:30 UTC3364
    #[test]
7ff8adafe9e4atagen2026-10-22 12:30 UTC3365
    fn test_openid_token_mapping() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC3366
        let response = ruma::api::client::account::request_openid_token::v3::Response::new(
7ff8adafe9e4atagen2026-10-22 12:30 UTC3367
            "open-id-token".to_owned(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC3368
            TokenType::Bearer,
7ff8adafe9e4atagen2026-10-22 12:30 UTC3369
            ServerName::parse("example.com").expect("valid server name"),
7ff8adafe9e4atagen2026-10-22 12:30 UTC3370
            Duration::from_secs(3_600),
7ff8adafe9e4atagen2026-10-22 12:30 UTC3371
        );
7ff8adafe9e4atagen2026-10-22 12:30 UTC3372
7ff8adafe9e4atagen2026-10-22 12:30 UTC3373
        let token: OpenIdToken = response.into();
7ff8adafe9e4atagen2026-10-22 12:30 UTC3374
7ff8adafe9e4atagen2026-10-22 12:30 UTC3375
        assert_eq!(token.access_token, "open-id-token");
7ff8adafe9e4atagen2026-10-22 12:30 UTC3376
        assert_eq!(token.token_type, "Bearer");
7ff8adafe9e4atagen2026-10-22 12:30 UTC3377
        assert_eq!(token.matrix_server_name, "example.com");
7ff8adafe9e4atagen2026-10-22 12:30 UTC3378
        assert_eq!(token.expires_in_seconds, 3_600);
7ff8adafe9e4atagen2026-10-22 12:30 UTC3379
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC3380
7ff8adafe9e4atagen2026-10-22 12:30 UTC3381
    /// Dropping an FFI [`Client`] on a non-tokio thread must not panic.
7ff8adafe9e4atagen2026-10-22 12:30 UTC3382
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC3383
    /// Regression test: `Client.inner` is wrapped in [`AsyncRuntimeDropped`]
7ff8adafe9e4atagen2026-10-22 12:30 UTC3384
    /// precisely because dropping a sqlite-backed [`MatrixClient`] outside a
7ff8adafe9e4atagen2026-10-22 12:30 UTC3385
    /// tokio runtime context causes `SyncWrapper<rusqlite::Connection>::drop`
7ff8adafe9e4atagen2026-10-22 12:30 UTC3386
    /// to call `tokio::task::spawn_blocking`, which panics and triggers
7ff8adafe9e4atagen2026-10-22 12:30 UTC3387
    /// SIGABRT. This test guards against accidentally removing that wrapper.
7ff8adafe9e4atagen2026-10-22 12:30 UTC3388
    #[cfg(not(target_family = "wasm"))]
7ff8adafe9e4atagen2026-10-22 12:30 UTC3389
    #[tokio::test]
7ff8adafe9e4atagen2026-10-22 12:30 UTC3390
    async fn client_drop_on_non_tokio_thread_does_not_panic() {
7ff8adafe9e4atagen2026-10-22 12:30 UTC3391
        use std::time::Duration;
7ff8adafe9e4atagen2026-10-22 12:30 UTC3392
7ff8adafe9e4atagen2026-10-22 12:30 UTC3393
        use matrix_sdk::{Client, config::RequestConfig};
7ff8adafe9e4atagen2026-10-22 12:30 UTC3394
        use matrix_sdk_common::cross_process_lock::CrossProcessLockConfig;
7ff8adafe9e4atagen2026-10-22 12:30 UTC3395
        use tempfile::tempdir;
7ff8adafe9e4atagen2026-10-22 12:30 UTC3396
7ff8adafe9e4atagen2026-10-22 12:30 UTC3397
        let dir = tempdir().unwrap();
7ff8adafe9e4atagen2026-10-22 12:30 UTC3398
7ff8adafe9e4atagen2026-10-22 12:30 UTC3399
        // SingleProcess lock avoids the session-delegate requirement in
7ff8adafe9e4atagen2026-10-22 12:30 UTC3400
        // `Client::new`, keeping the test self-contained.
7ff8adafe9e4atagen2026-10-22 12:30 UTC3401
        let sdk_client = Client::builder()
7ff8adafe9e4atagen2026-10-22 12:30 UTC3402
            .homeserver_url("https://example.com")
7ff8adafe9e4atagen2026-10-22 12:30 UTC3403
            .request_config(RequestConfig::new().disable_retry())
7ff8adafe9e4atagen2026-10-22 12:30 UTC3404
            .sqlite_store(dir.path(), None)
7ff8adafe9e4atagen2026-10-22 12:30 UTC3405
            .cross_process_store_config(CrossProcessLockConfig::SingleProcess)
7ff8adafe9e4atagen2026-10-22 12:30 UTC3406
            .build()
7ff8adafe9e4atagen2026-10-22 12:30 UTC3407
            .await
7ff8adafe9e4atagen2026-10-22 12:30 UTC3408
            .unwrap();
7ff8adafe9e4atagen2026-10-22 12:30 UTC3409
7ff8adafe9e4atagen2026-10-22 12:30 UTC3410
        // Allow background init tasks to complete; after this the only strong
7ff8adafe9e4atagen2026-10-22 12:30 UTC3411
        // reference to `ClientInner` is the local `sdk_client` variable.
7ff8adafe9e4atagen2026-10-22 12:30 UTC3412
        tokio::time::sleep(Duration::from_secs(1)).await;
7ff8adafe9e4atagen2026-10-22 12:30 UTC3413
7ff8adafe9e4atagen2026-10-22 12:30 UTC3414
        let ffi_client = super::Client::new(sdk_client.clone(), None, None).await.unwrap();
7ff8adafe9e4atagen2026-10-22 12:30 UTC3415
7ff8adafe9e4atagen2026-10-22 12:30 UTC3416
        // Dropping `sdk_client` leaves `ffi_client` as the sole Arc holder of
7ff8adafe9e4atagen2026-10-22 12:30 UTC3417
        // `ClientInner` — mirroring what happens when the last JS reference to
7ff8adafe9e4atagen2026-10-22 12:30 UTC3418
        // a Client is garbage-collected on the Hermes JS thread.
7ff8adafe9e4atagen2026-10-22 12:30 UTC3419
        drop(sdk_client);
7ff8adafe9e4atagen2026-10-22 12:30 UTC3420
7ff8adafe9e4atagen2026-10-22 12:30 UTC3421
        // Simulate Hermes GC on the JS thread (a non-tokio thread).
7ff8adafe9e4atagen2026-10-22 12:30 UTC3422
        // Without `AsyncRuntimeDropped` wrapping `Client.inner` this SIGABRT.
7ff8adafe9e4atagen2026-10-22 12:30 UTC3423
        std::thread::spawn(move || drop(ffi_client))
7ff8adafe9e4atagen2026-10-22 12:30 UTC3424
            .join()
7ff8adafe9e4atagen2026-10-22 12:30 UTC3425
            .expect("Client::drop panicked on a non-tokio thread");
7ff8adafe9e4atagen2026-10-22 12:30 UTC3426
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC3427
}