matrix-ui-serializable 0.4.0

Opinionated abstraction of the matrix-sdk crate with serializable structs
Documentation
use anyhow::anyhow;
use matrix_sdk::{
    Client, ThreadingSupport, config::RequestConfig, encryption::EncryptionSettings,
    sliding_sync::VersionBuilder,
};

use rand::{RngExt, distr::Alphanumeric, rng};

use crate::{
    CLIENT,
    events::handlers::add_event_handlers,
    init::singletons::{APP_DATA_DIR, TEMP_CLIENT, TEMP_CLIENT_SESSION},
};

use super::session::ClientSession;

pub(crate) async fn login_and_persist_matrix_session(
    client: &Client,
    username: String,
    password: String,
    device_name: String,
) -> anyhow::Result<String> {
    let matrix_auth = client.matrix_auth();

    matrix_auth
        .login_username(username, &password)
        .initial_device_display_name(&device_name)
        .await?;

    let user_session = matrix_auth
        .session()
        .expect("Should have session after login");

    let full = super::session::FullMatrixSession::new(
        TEMP_CLIENT_SESSION
            .lock()
            .unwrap()
            .clone()
            .ok_or(anyhow!("No temporary session has been set !"))?,
        matrix_sdk::AuthSession::Matrix(user_session),
    );

    CLIENT
        .set(
            TEMP_CLIENT
                .lock
                .lock()
                .unwrap()
                .clone()
                .ok_or(anyhow!("No temporary client was set !"))?,
        )
        .expect("Client already set !");
    let serialized = serde_json::to_string(&full)?;

    Ok(serialized)
}

pub async fn build_client(
    homeserver_opt: Option<String>,
    client_session: Option<ClientSession>,
) -> anyhow::Result<(Client, ClientSession)> {
    let (homeserver, db_path, passphrase, db_identifier) = match client_session {
        Some(s) => {
            let db_path = APP_DATA_DIR
                .wait()
                .clone()
                .join("matrix-db")
                .join(&s.db_identifier);
            (s.homeserver, db_path, s.passphrase, s.db_identifier)
        }
        None => {
            if let Some(homeserver) = homeserver_opt {
                let db_identifier: String = rng()
                    .sample_iter(Alphanumeric)
                    .take(7)
                    .map(char::from)
                    .collect();
                let db_path = APP_DATA_DIR
                    .wait()
                    .clone()
                    .join("matrix-db")
                    .join(&db_identifier);

                std::fs::create_dir_all(&db_path)?;

                let passphrase: String = rng()
                    .sample_iter(Alphanumeric)
                    .take(32)
                    .map(char::from)
                    .collect();
                (homeserver, db_path, passphrase, db_identifier)
            } else {
                panic!("Cannot recover session from storage or create it without homeserver")
            }
        }
    };

    let client = Client::builder()
        .server_name_or_homeserver_url(homeserver.clone())
        .with_threading_support(ThreadingSupport::Enabled {
            with_subscriptions: false,
        })
        .sqlite_store(&db_path, Some(&passphrase))
        .sliding_sync_version_builder(VersionBuilder::DiscoverNative)
        .with_encryption_settings(EncryptionSettings {
            auto_enable_cross_signing: true,
            backup_download_strategy: matrix_sdk::encryption::BackupDownloadStrategy::OneShot,
            auto_enable_backups: true,
        })
        .with_enable_share_history_on_invite(true)
        .handle_refresh_tokens()
        .request_config(RequestConfig::new().timeout(std::time::Duration::from_secs(60)))
        .build()
        .await?;

    add_event_handlers(&client);

    let client_session = ClientSession::new(homeserver, db_identifier, passphrase);

    Ok((client, client_session))
}