|
|
|
@ -477,7 +477,7 @@ impl Client {
|
|
|
|
|
/// If this isn't the first login a device id should be provided to restore
|
|
|
|
|
/// the correct stores.
|
|
|
|
|
///
|
|
|
|
|
/// Alternatively the `restore_login()` method can be used to restore a
|
|
|
|
|
/// Alternatively the [`restore_login`] method can be used to restore a
|
|
|
|
|
/// logged in client without the password.
|
|
|
|
|
///
|
|
|
|
|
/// # Arguments
|
|
|
|
@ -490,6 +490,27 @@ impl Client {
|
|
|
|
|
/// not given the homeserver will create one. Can be an existing
|
|
|
|
|
/// device_id from a previous login call. Note that this should be done
|
|
|
|
|
/// only if the client also holds the encryption keys for this device.
|
|
|
|
|
///
|
|
|
|
|
/// # Example
|
|
|
|
|
/// ```no_run
|
|
|
|
|
/// # use std::convert::TryFrom;
|
|
|
|
|
/// # use matrix_sdk::Client;
|
|
|
|
|
/// # use matrix_sdk::identifiers::DeviceId;
|
|
|
|
|
/// # use matrix_sdk_common::assign;
|
|
|
|
|
/// # use futures::executor::block_on;
|
|
|
|
|
/// # use url::Url;
|
|
|
|
|
/// # let homeserver = Url::parse("http://example.com").unwrap();
|
|
|
|
|
/// # block_on(async {
|
|
|
|
|
/// let client = Client::new(homeserver).unwrap();
|
|
|
|
|
/// let user = "example";
|
|
|
|
|
/// let response = client.login(user, "wordpass", None, Some("My bot")).await;
|
|
|
|
|
///
|
|
|
|
|
/// println!("Logged in as {}, got device_id {} and access_token {}",
|
|
|
|
|
/// user, response.device_id, response.access_token);
|
|
|
|
|
/// # })
|
|
|
|
|
/// ```
|
|
|
|
|
///
|
|
|
|
|
/// [`restore_login`]: #method.restore_login
|
|
|
|
|
#[instrument(skip(password))]
|
|
|
|
|
pub async fn login(
|
|
|
|
|
&self,
|
|
|
|
@ -521,13 +542,15 @@ impl Client {
|
|
|
|
|
/// This can be used to restore the client to a logged in state, loading all
|
|
|
|
|
/// the stored state and encryption keys.
|
|
|
|
|
///
|
|
|
|
|
/// Alternatively, if the whole session isn't stored the `login()` method
|
|
|
|
|
/// Alternatively, if the whole session isn't stored the [`login`] method
|
|
|
|
|
/// can be used with a device id.
|
|
|
|
|
///
|
|
|
|
|
/// # Arguments
|
|
|
|
|
///
|
|
|
|
|
/// * `session` - A session that the user already has from a
|
|
|
|
|
/// previous login call.
|
|
|
|
|
///
|
|
|
|
|
/// [`login`]: #method.login
|
|
|
|
|
pub async fn restore_login(&self, session: Session) -> Result<()> {
|
|
|
|
|
Ok(self.base_client.restore_login(session).await?)
|
|
|
|
|
}
|
|
|
|
@ -1345,14 +1368,20 @@ impl Client {
|
|
|
|
|
|
|
|
|
|
/// Synchronize the client's state with the latest state on the server.
|
|
|
|
|
///
|
|
|
|
|
/// If a `StateStore` is provided and this is the initial sync state will
|
|
|
|
|
/// be loaded from the state store.
|
|
|
|
|
/// **Note**: You should not use this method to repeatedly sync if encryption
|
|
|
|
|
/// support is enabled, the [`sync`] method will make additional
|
|
|
|
|
/// requests between syncs that are needed for E2E encryption to work.
|
|
|
|
|
///
|
|
|
|
|
/// # Arguments
|
|
|
|
|
///
|
|
|
|
|
/// * `sync_settings` - Settings for the sync call.
|
|
|
|
|
///
|
|
|
|
|
/// [`sync`]: #method.sync
|
|
|
|
|
#[instrument]
|
|
|
|
|
pub async fn sync(&self, sync_settings: SyncSettings<'_>) -> Result<sync_events::Response> {
|
|
|
|
|
pub async fn sync_once(
|
|
|
|
|
&self,
|
|
|
|
|
sync_settings: SyncSettings<'_>,
|
|
|
|
|
) -> Result<sync_events::Response> {
|
|
|
|
|
let request = assign!(sync_events::Request::new(), {
|
|
|
|
|
filter: sync_settings.filter,
|
|
|
|
|
since: sync_settings.token.as_deref(),
|
|
|
|
@ -1370,6 +1399,23 @@ impl Client {
|
|
|
|
|
Ok(response)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Repeatedly call sync to synchronize the client state with the server.
|
|
|
|
|
///
|
|
|
|
|
/// This method will never return, if cancellation is needed the method
|
|
|
|
|
/// should be wrapped in a cancelable task or the [`sync_with_callback`]
|
|
|
|
|
/// method can be used.
|
|
|
|
|
///
|
|
|
|
|
/// # Arguments
|
|
|
|
|
///
|
|
|
|
|
/// * `sync_settings` - Settings for the sync call. Note that those settings
|
|
|
|
|
/// will be only used for the first sync call.
|
|
|
|
|
///
|
|
|
|
|
/// [`sync_with_callback`]: #method.sync_with_callback
|
|
|
|
|
pub async fn sync(&self, sync_settings: SyncSettings<'_>) {
|
|
|
|
|
self.sync_with_callback(sync_settings, |_| async { false })
|
|
|
|
|
.await
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Repeatedly call sync to synchronize the client state with the server.
|
|
|
|
|
///
|
|
|
|
|
/// # Arguments
|
|
|
|
@ -1378,7 +1424,10 @@ impl Client {
|
|
|
|
|
/// will be only used for the first sync call.
|
|
|
|
|
///
|
|
|
|
|
/// * `callback` - A callback that will be called every time a successful
|
|
|
|
|
/// response has been fetched from the server.
|
|
|
|
|
/// response has been fetched from the server. The callback must return
|
|
|
|
|
/// a boolean which signalizes if the method should stop syncing. If the
|
|
|
|
|
/// callback returns `false` the sync will continue, if the callback
|
|
|
|
|
/// returns `true` the sync will be stopped.
|
|
|
|
|
///
|
|
|
|
|
/// # Examples
|
|
|
|
|
///
|
|
|
|
@ -1421,17 +1470,19 @@ impl Client {
|
|
|
|
|
/// }
|
|
|
|
|
/// }
|
|
|
|
|
/// }
|
|
|
|
|
///
|
|
|
|
|
/// false
|
|
|
|
|
/// })
|
|
|
|
|
/// .await;
|
|
|
|
|
/// })
|
|
|
|
|
/// ```
|
|
|
|
|
#[instrument(skip(callback))]
|
|
|
|
|
pub async fn sync_forever<C>(
|
|
|
|
|
pub async fn sync_with_callback<C>(
|
|
|
|
|
&self,
|
|
|
|
|
sync_settings: SyncSettings<'_>,
|
|
|
|
|
callback: impl Fn(sync_events::Response) -> C,
|
|
|
|
|
) where
|
|
|
|
|
C: Future<Output = ()>,
|
|
|
|
|
C: Future<Output = bool>,
|
|
|
|
|
{
|
|
|
|
|
let mut sync_settings = sync_settings;
|
|
|
|
|
let filter = sync_settings.filter;
|
|
|
|
@ -1442,7 +1493,7 @@ impl Client {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
loop {
|
|
|
|
|
let response = self.sync(sync_settings.clone()).await;
|
|
|
|
|
let response = self.sync_once(sync_settings.clone()).await;
|
|
|
|
|
|
|
|
|
|
let response = match response {
|
|
|
|
|
Ok(r) => r,
|
|
|
|
@ -1483,7 +1534,9 @@ impl Client {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
callback(response).await;
|
|
|
|
|
if callback(response).await {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let now = Instant::now();
|
|
|
|
|
|
|
|
|
@ -1781,7 +1834,10 @@ impl Client {
|
|
|
|
|
/// ```
|
|
|
|
|
#[cfg(feature = "encryption")]
|
|
|
|
|
#[cfg(not(target_arch = "wasm32"))]
|
|
|
|
|
#[cfg_attr(feature = "docs", doc(cfg(encryption)))]
|
|
|
|
|
#[cfg_attr(
|
|
|
|
|
feature = "docs",
|
|
|
|
|
doc(cfg(all(encryption, not(target_arch = "wasm32"))))
|
|
|
|
|
)]
|
|
|
|
|
pub async fn export_keys(
|
|
|
|
|
&self,
|
|
|
|
|
path: PathBuf,
|
|
|
|
@ -1831,7 +1887,10 @@ impl Client {
|
|
|
|
|
/// ```
|
|
|
|
|
#[cfg(feature = "encryption")]
|
|
|
|
|
#[cfg(not(target_arch = "wasm32"))]
|
|
|
|
|
#[cfg_attr(feature = "docs", doc(cfg(encryption)))]
|
|
|
|
|
#[cfg_attr(
|
|
|
|
|
feature = "docs",
|
|
|
|
|
doc(cfg(all(encryption, not(target_arch = "wasm32"))))
|
|
|
|
|
)]
|
|
|
|
|
pub async fn import_keys(&self, path: PathBuf, passphrase: &str) -> Result<()> {
|
|
|
|
|
let olm = self
|
|
|
|
|
.base_client
|
|
|
|
@ -1959,7 +2018,7 @@ mod test {
|
|
|
|
|
let room = client.get_joined_room(&room_id).await;
|
|
|
|
|
assert!(room.is_none());
|
|
|
|
|
|
|
|
|
|
client.sync(SyncSettings::default()).await.unwrap();
|
|
|
|
|
client.sync_once(SyncSettings::default()).await.unwrap();
|
|
|
|
|
|
|
|
|
|
let room = client.get_left_room(&room_id).await;
|
|
|
|
|
assert!(room.is_none());
|
|
|
|
@ -1974,7 +2033,10 @@ mod test {
|
|
|
|
|
joined_client.restore_login(session).await.unwrap();
|
|
|
|
|
|
|
|
|
|
// joined room reloaded from state store
|
|
|
|
|
joined_client.sync(SyncSettings::default()).await.unwrap();
|
|
|
|
|
joined_client
|
|
|
|
|
.sync_once(SyncSettings::default())
|
|
|
|
|
.await
|
|
|
|
|
.unwrap();
|
|
|
|
|
let room = joined_client.get_joined_room(&room_id).await;
|
|
|
|
|
assert!(room.is_some());
|
|
|
|
|
|
|
|
|
@ -1986,7 +2048,10 @@ mod test {
|
|
|
|
|
.with_body(test_json::LEAVE_SYNC_EVENT.to_string())
|
|
|
|
|
.create();
|
|
|
|
|
|
|
|
|
|
joined_client.sync(SyncSettings::default()).await.unwrap();
|
|
|
|
|
joined_client
|
|
|
|
|
.sync_once(SyncSettings::default())
|
|
|
|
|
.await
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
|
|
let room = joined_client.get_joined_room(&room_id).await;
|
|
|
|
|
assert!(room.is_none());
|
|
|
|
@ -2009,7 +2074,7 @@ mod test {
|
|
|
|
|
.create();
|
|
|
|
|
|
|
|
|
|
let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000));
|
|
|
|
|
let _response = client.sync(sync_settings).await.unwrap();
|
|
|
|
|
let _response = client.sync_once(sync_settings).await.unwrap();
|
|
|
|
|
|
|
|
|
|
// let bc = &client.base_client;
|
|
|
|
|
// let ignored_users = bc.ignored_users.read().await;
|
|
|
|
@ -2478,7 +2543,7 @@ mod test {
|
|
|
|
|
|
|
|
|
|
let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000));
|
|
|
|
|
|
|
|
|
|
let _response = client.sync(sync_settings).await.unwrap();
|
|
|
|
|
let _response = client.sync_once(sync_settings).await.unwrap();
|
|
|
|
|
|
|
|
|
|
let rooms_lock = &client.base_client.joined_rooms();
|
|
|
|
|
let rooms = rooms_lock.read().await;
|
|
|
|
@ -2506,7 +2571,7 @@ mod test {
|
|
|
|
|
.create();
|
|
|
|
|
|
|
|
|
|
let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000));
|
|
|
|
|
let _response = client.sync(sync_settings).await.unwrap();
|
|
|
|
|
let _response = client.sync_once(sync_settings).await.unwrap();
|
|
|
|
|
|
|
|
|
|
let mut room_names = vec![];
|
|
|
|
|
for room in client.joined_rooms().read().await.values() {
|
|
|
|
@ -2529,7 +2594,7 @@ mod test {
|
|
|
|
|
.with_body(test_json::INVITE_SYNC.to_string())
|
|
|
|
|
.create();
|
|
|
|
|
|
|
|
|
|
let _response = client.sync(SyncSettings::default()).await.unwrap();
|
|
|
|
|
let _response = client.sync_once(SyncSettings::default()).await.unwrap();
|
|
|
|
|
|
|
|
|
|
assert!(client.joined_rooms().read().await.is_empty());
|
|
|
|
|
assert!(client.left_rooms().read().await.is_empty());
|
|
|
|
@ -2554,7 +2619,7 @@ mod test {
|
|
|
|
|
.with_body(test_json::LEAVE_SYNC.to_string())
|
|
|
|
|
.create();
|
|
|
|
|
|
|
|
|
|
let _response = client.sync(SyncSettings::default()).await.unwrap();
|
|
|
|
|
let _response = client.sync_once(SyncSettings::default()).await.unwrap();
|
|
|
|
|
|
|
|
|
|
assert!(client.joined_rooms().read().await.is_empty());
|
|
|
|
|
assert!(!client.left_rooms().read().await.is_empty());
|
|
|
|
@ -2598,14 +2663,14 @@ mod test {
|
|
|
|
|
let sync_settings = SyncSettings::new().timeout(std::time::Duration::from_millis(3000));
|
|
|
|
|
|
|
|
|
|
// gather state to save to the db, the first time through loading will be skipped
|
|
|
|
|
let _ = client.sync(sync_settings.clone()).await.unwrap();
|
|
|
|
|
let _ = client.sync_once(sync_settings.clone()).await.unwrap();
|
|
|
|
|
|
|
|
|
|
// now syncing the client will update from the state store
|
|
|
|
|
let config =
|
|
|
|
|
ClientConfig::default().state_store(Box::new(JsonStore::open(dir.path()).unwrap()));
|
|
|
|
|
let client = Client::new_with_config(homeserver, config).unwrap();
|
|
|
|
|
client.restore_login(session.clone()).await.unwrap();
|
|
|
|
|
client.sync(sync_settings).await.unwrap();
|
|
|
|
|
client.sync_once(sync_settings).await.unwrap();
|
|
|
|
|
|
|
|
|
|
let base_client = &client.base_client;
|
|
|
|
|
|
|
|
|
@ -2638,7 +2703,7 @@ mod test {
|
|
|
|
|
|
|
|
|
|
let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000));
|
|
|
|
|
|
|
|
|
|
let response = client.sync(sync_settings).await.unwrap();
|
|
|
|
|
let response = client.sync_once(sync_settings).await.unwrap();
|
|
|
|
|
|
|
|
|
|
assert_ne!(response.next_batch, "");
|
|
|
|
|
|
|
|
|
@ -2660,7 +2725,7 @@ mod test {
|
|
|
|
|
|
|
|
|
|
let sync_settings = SyncSettings::new().timeout(Duration::from_millis(3000));
|
|
|
|
|
|
|
|
|
|
let _response = client.sync(sync_settings).await.unwrap();
|
|
|
|
|
let _response = client.sync_once(sync_settings).await.unwrap();
|
|
|
|
|
|
|
|
|
|
let mut names = vec![];
|
|
|
|
|
for r in client.joined_rooms().read().await.values() {
|
|
|
|
|