Skip to content

Commit 5465a7b

Browse files
committed
matrix-sdk: prevent frequent typing_notice requests
1 parent 2f76972 commit 5465a7b

File tree

2 files changed

+50
-18
lines changed

2 files changed

+50
-18
lines changed

matrix_sdk/src/client.rs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ pub struct Client {
138138
/// Lock making sure we're only doing one key claim request at a time.
139139
key_claim_lock: Arc<Mutex<()>>,
140140
pub(crate) members_request_locks: DashMap<RoomId, Arc<Mutex<()>>>,
141+
pub(crate) typing_notice_times: DashMap<RoomId, Instant>,
141142
}
142143

143144
#[cfg(not(tarpaulin_include))]
@@ -394,6 +395,7 @@ impl Client {
394395
#[cfg(feature = "encryption")]
395396
key_claim_lock: Arc::new(Mutex::new(())),
396397
members_request_locks: DashMap::new(),
398+
typing_notice_times: DashMap::new(),
397399
})
398400
}
399401

@@ -1669,7 +1671,6 @@ impl Client {
16691671
/// # use std::{path::PathBuf, time::Duration};
16701672
/// # use matrix_sdk::{
16711673
/// # Client, SyncSettings,
1672-
/// # api::r0::typing::create_typing_event::Typing,
16731674
/// # identifiers::room_id,
16741675
/// # };
16751676
/// # use futures::executor::block_on;
@@ -1747,7 +1748,6 @@ impl Client {
17471748
/// # use std::{path::PathBuf, time::Duration};
17481749
/// # use matrix_sdk::{
17491750
/// # Client, SyncSettings,
1750-
/// # api::r0::typing::create_typing_event::Typing,
17511751
/// # identifiers::room_id,
17521752
/// # };
17531753
/// # use futures::executor::block_on;
@@ -1802,7 +1802,7 @@ mod test {
18021802
api::r0::{
18031803
account::register::Request as RegistrationRequest,
18041804
directory::get_public_rooms_filtered::Request as PublicRoomsFilterRequest,
1805-
membership::Invite3pid, typing::create_typing_event::Typing, uiaa::AuthData,
1805+
membership::Invite3pid, uiaa::AuthData,
18061806
},
18071807
assign,
18081808
directory::Filter,
@@ -2451,9 +2451,7 @@ mod test {
24512451
.get_joined_room(&room_id!("!SVkFJHzfwvuaIEawgC:localhost"))
24522452
.unwrap();
24532453

2454-
room.typing_notice(Typing::Yes(std::time::Duration::from_secs(1)))
2455-
.await
2456-
.unwrap();
2454+
room.typing_notice(true).await.unwrap();
24572455
}
24582456

24592457
#[tokio::test]

matrix_sdk/src/room/joined.rs

Lines changed: 46 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ use matrix_sdk_common::{
2626
AnyMessageEventContent, AnyStateEventContent,
2727
},
2828
identifiers::{EventId, UserId},
29+
instant::{Duration, Instant},
2930
uuid::Uuid,
3031
};
3132

@@ -40,6 +41,9 @@ use matrix_sdk_base::crypto::AttachmentEncryptor;
4041
#[cfg(feature = "encryption")]
4142
use tracing::instrument;
4243

44+
const TYPING_NOTICE_TIMEOUT: Duration = Duration::from_secs(4);
45+
const TYPING_NOTICE_RESEND_TIMEOUT: Duration = Duration::from_secs(3);
46+
4347
/// A room in the joined state.
4448
///
4549
/// The `JoinedRoom` contains all methodes specific to a `Room` with type `RoomType::Joined`.
@@ -137,19 +141,23 @@ impl Joined {
137141
Ok(())
138142
}
139143

140-
/// Send a request to notify this room of a user typing.
144+
/// Activate typing notice for this room.
145+
///
146+
/// The typing notice remains active for 4s. It can be deactivate at any point by setting
147+
/// typing to `false`. If this method is called while the typing notice is active nothing will happen.
148+
/// This method can be called on every key stroke, since it will do nothing while typing is
149+
/// active.
141150
///
142151
/// # Arguments
143152
///
144-
/// * `typing` - Whether the user is typing, and how long.
153+
/// * `typing` - Whether the user is typing or has stopped typing.
145154
///
146155
/// # Examples
147156
///
148157
/// ```no_run
149158
/// # use std::time::Duration;
150159
/// # use matrix_sdk::{
151160
/// # Client, SyncSettings,
152-
/// # api::r0::typing::create_typing_event::Typing,
153161
/// # identifiers::room_id,
154162
/// # };
155163
/// # use futures::executor::block_on;
@@ -162,21 +170,47 @@ impl Joined {
162170
/// # .get_joined_room(&room_id!("!SVkFJHzfwvuaIEawgC:localhost"))
163171
/// # .unwrap();
164172
/// # room
165-
/// .typing_notice(Typing::Yes(Duration::from_secs(4)))
173+
/// .typing_notice(true)
166174
/// .await
167175
/// .expect("Can't get devices from server");
168176
/// # });
169177
///
170178
/// ```
171-
pub async fn typing_notice(&self, typing: impl Into<Typing>) -> Result<()> {
172-
// TODO: don't send a request if a typing notice is being sent or is already active
173-
let request = TypingRequest::new(
174-
self.inner.own_user_id(),
175-
self.inner.room_id(),
176-
typing.into(),
177-
);
179+
pub async fn typing_notice(&self, typing: bool) -> Result<()> {
180+
// Only send a request to the homeserver if the old timeout has elapsed or the typing
181+
// notice changed state within the TYPING_NOTICE_TIMEOUT
182+
let send =
183+
if let Some(typing_time) = self.client.typing_notice_times.get(self.inner.room_id()) {
184+
if typing_time.elapsed() > TYPING_NOTICE_RESEND_TIMEOUT {
185+
// We always reactivate the typing notice if typing is true or we may need to
186+
// deactivate it if it's currently active if typing is false
187+
typing || typing_time.elapsed() <= TYPING_NOTICE_TIMEOUT
188+
} else {
189+
// Only send a request when we need to deactivate typing
190+
!typing
191+
}
192+
} else {
193+
// Typing notice is currently deactivated, therefore, send a request only when it's
194+
// about to be activated
195+
typing
196+
};
197+
198+
if send {
199+
let typing = if typing {
200+
self.client
201+
.typing_notice_times
202+
.insert(self.inner.room_id().clone(), Instant::now());
203+
Typing::Yes(TYPING_NOTICE_TIMEOUT)
204+
} else {
205+
self.client.typing_notice_times.remove(self.inner.room_id());
206+
Typing::No
207+
};
208+
209+
let request =
210+
TypingRequest::new(self.inner.own_user_id(), self.inner.room_id(), typing);
211+
self.client.send(request, None).await?;
212+
}
178213

179-
self.client.send(request, None).await?;
180214
Ok(())
181215
}
182216

0 commit comments

Comments
 (0)