feat(base): ignore threaded messages when computing room read receipts and client wide threads feature flag
With the UI crate now sending threaded read receipts we need to start considering them when computing unread counts. As a first step before the participation model, threaded messages will be ignored when computing room unread counts.
This commit is contained in:
committed by
Stefan Ceriu
parent
f17c3c5af4
commit
b44a1e46c4
@@ -4,6 +4,7 @@ use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Through
|
||||
use matrix_sdk::{store::RoomLoadSettings, test_utils::mocks::MatrixMockServer};
|
||||
use matrix_sdk_base::{
|
||||
store::StoreConfig, BaseClient, RoomInfo, RoomState, SessionMeta, StateChanges, StateStore,
|
||||
ThreadingSupport,
|
||||
};
|
||||
use matrix_sdk_sqlite::SqliteStateStore;
|
||||
use matrix_sdk_test::{event_factory::EventFactory, JoinedRoomBuilder, StateTestEvent};
|
||||
@@ -58,6 +59,7 @@ pub fn receive_all_members_benchmark(c: &mut Criterion) {
|
||||
let base_client = BaseClient::new(
|
||||
StoreConfig::new("cross-process-store-locks-holder-name".to_owned())
|
||||
.state_store(sqlite_store),
|
||||
ThreadingSupport::Disabled,
|
||||
);
|
||||
|
||||
runtime
|
||||
|
||||
@@ -15,7 +15,7 @@ use matrix_sdk::{
|
||||
VersionBuilderError,
|
||||
},
|
||||
Client as MatrixClient, ClientBuildError as MatrixClientBuildError, HttpError, IdParseError,
|
||||
RumaApiError, SqliteStoreConfig,
|
||||
RumaApiError, SqliteStoreConfig, ThreadingSupport,
|
||||
};
|
||||
use ruma::api::error::{DeserializationError, FromHttpResponseError};
|
||||
use tracing::{debug, error};
|
||||
@@ -140,6 +140,8 @@ pub struct ClientBuilder {
|
||||
disable_built_in_root_certificates: bool,
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
additional_root_certificates: Vec<Vec<u8>>,
|
||||
|
||||
threads_enabled: bool,
|
||||
}
|
||||
|
||||
#[matrix_sdk_ffi_macros::export]
|
||||
@@ -177,6 +179,7 @@ impl ClientBuilder {
|
||||
},
|
||||
enable_share_history_on_invite: false,
|
||||
request_config: Default::default(),
|
||||
threads_enabled: false,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -390,6 +393,12 @@ impl ClientBuilder {
|
||||
Arc::new(builder)
|
||||
}
|
||||
|
||||
pub fn threads_enabled(self: Arc<Self>, enabled: bool) -> Arc<Self> {
|
||||
let mut builder = unwrap_or_clone_arc(self);
|
||||
builder.threads_enabled = enabled;
|
||||
Arc::new(builder)
|
||||
}
|
||||
|
||||
pub async fn build(self: Arc<Self>) -> Result<Arc<Client>, ClientBuildError> {
|
||||
let builder = unwrap_or_clone_arc(self);
|
||||
let mut inner_builder = MatrixClient::builder();
|
||||
@@ -555,6 +564,12 @@ impl ClientBuilder {
|
||||
inner_builder = inner_builder.request_config(updated_config);
|
||||
}
|
||||
|
||||
inner_builder = inner_builder.with_threading_support(if builder.threads_enabled {
|
||||
ThreadingSupport::Enabled
|
||||
} else {
|
||||
ThreadingSupport::Disabled
|
||||
});
|
||||
|
||||
let sdk_client = inner_builder.build().await?;
|
||||
|
||||
Ok(Arc::new(
|
||||
|
||||
@@ -76,11 +76,12 @@ use crate::{
|
||||
/// rather through `matrix_sdk::Client`.
|
||||
///
|
||||
/// ```rust
|
||||
/// use matrix_sdk_base::{store::StoreConfig, BaseClient};
|
||||
/// use matrix_sdk_base::{store::StoreConfig, BaseClient, ThreadingSupport};
|
||||
///
|
||||
/// let client = BaseClient::new(StoreConfig::new(
|
||||
/// "cross-process-holder-name".to_owned(),
|
||||
/// ));
|
||||
/// let client = BaseClient::new(
|
||||
/// StoreConfig::new("cross-process-holder-name".to_owned()),
|
||||
/// ThreadingSupport::Disabled,
|
||||
/// );
|
||||
/// ```
|
||||
#[derive(Clone)]
|
||||
pub struct BaseClient {
|
||||
@@ -122,6 +123,9 @@ pub struct BaseClient {
|
||||
/// If the client should handle verification events received when syncing.
|
||||
#[cfg(feature = "e2e-encryption")]
|
||||
pub handle_verification_events: bool,
|
||||
|
||||
/// Whether the client supports threads or not.
|
||||
pub threading_support: ThreadingSupport,
|
||||
}
|
||||
|
||||
#[cfg(not(tarpaulin_include))]
|
||||
@@ -134,6 +138,25 @@ impl fmt::Debug for BaseClient {
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether this client instance supports threading or not. Currently used to
|
||||
/// determine how the client handles read receipts and unread count computations
|
||||
/// on the base SDK level.
|
||||
///
|
||||
/// Timelines on the other hand have a separate `TimelineFocus`
|
||||
/// `hide_threaded_events` associated value that can be used to hide threaded
|
||||
/// events but also to enable threaded read receipt sending. This is because
|
||||
/// certain timeline instances should ignore threading no matter what's defined
|
||||
/// at the client level. One such example are media filtered timelines which
|
||||
/// should contain all the room's media no matter what thread its in (unless
|
||||
/// explicitly opted into).
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum ThreadingSupport {
|
||||
/// Threading enabled
|
||||
Enabled,
|
||||
/// Threading disabled
|
||||
Disabled,
|
||||
}
|
||||
|
||||
impl BaseClient {
|
||||
/// Create a new client.
|
||||
///
|
||||
@@ -141,7 +164,7 @@ impl BaseClient {
|
||||
///
|
||||
/// * `config` - the configuration for the stores (state store, event cache
|
||||
/// store and crypto store).
|
||||
pub fn new(config: StoreConfig) -> Self {
|
||||
pub fn new(config: StoreConfig, threading_support: ThreadingSupport) -> Self {
|
||||
let store = BaseStateStore::new(config.state_store);
|
||||
|
||||
// Create the channel to receive `RoomInfoNotableUpdate`.
|
||||
@@ -173,6 +196,7 @@ impl BaseClient {
|
||||
},
|
||||
#[cfg(feature = "e2e-encryption")]
|
||||
handle_verification_events: true,
|
||||
threading_support,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,6 +228,7 @@ impl BaseClient {
|
||||
room_key_recipient_strategy: self.room_key_recipient_strategy.clone(),
|
||||
decryption_settings: self.decryption_settings.clone(),
|
||||
handle_verification_events,
|
||||
threading_support: self.threading_support,
|
||||
};
|
||||
|
||||
copy.state_store
|
||||
@@ -224,7 +249,7 @@ impl BaseClient {
|
||||
) -> Result<Self> {
|
||||
let config = StoreConfig::new(cross_process_store_locks_holder.to_owned())
|
||||
.state_store(MemoryStore::new());
|
||||
Ok(Self::new(config))
|
||||
Ok(Self::new(config, ThreadingSupport::Disabled))
|
||||
}
|
||||
|
||||
/// Get the session meta information.
|
||||
@@ -1080,6 +1105,7 @@ mod tests {
|
||||
|
||||
use super::{BaseClient, RequestedRequiredStates};
|
||||
use crate::{
|
||||
client::ThreadingSupport,
|
||||
store::{RoomLoadSettings, StateStoreExt, StoreConfig},
|
||||
test_utils::logged_in_base_client,
|
||||
RoomDisplayName, RoomState, SessionMeta,
|
||||
@@ -1374,8 +1400,10 @@ mod tests {
|
||||
let user_id = user_id!("@alice:example.org");
|
||||
let room_id = room_id!("!ithpyNKDtmhneaTQja:example.org");
|
||||
|
||||
let client =
|
||||
BaseClient::new(StoreConfig::new("cross-process-store-locks-holder-name".to_owned()));
|
||||
let client = BaseClient::new(
|
||||
StoreConfig::new("cross-process-store-locks-holder-name".to_owned()),
|
||||
ThreadingSupport::Disabled,
|
||||
);
|
||||
client
|
||||
.activate(
|
||||
SessionMeta { user_id: user_id.to_owned(), device_id: "FOOBAR".into() },
|
||||
@@ -1434,8 +1462,10 @@ mod tests {
|
||||
let inviter_user_id = user_id!("@bob:example.org");
|
||||
let room_id = room_id!("!ithpyNKDtmhneaTQja:example.org");
|
||||
|
||||
let client =
|
||||
BaseClient::new(StoreConfig::new("cross-process-store-locks-holder-name".to_owned()));
|
||||
let client = BaseClient::new(
|
||||
StoreConfig::new("cross-process-store-locks-holder-name".to_owned()),
|
||||
ThreadingSupport::Disabled,
|
||||
);
|
||||
client
|
||||
.activate(
|
||||
SessionMeta { user_id: user_id.to_owned(), device_id: "FOOBAR".into() },
|
||||
@@ -1496,8 +1526,10 @@ mod tests {
|
||||
let inviter_user_id = user_id!("@bob:example.org");
|
||||
let room_id = room_id!("!ithpyNKDtmhneaTQja:example.org");
|
||||
|
||||
let client =
|
||||
BaseClient::new(StoreConfig::new("cross-process-store-locks-holder-name".to_owned()));
|
||||
let client = BaseClient::new(
|
||||
StoreConfig::new("cross-process-store-locks-holder-name".to_owned()),
|
||||
ThreadingSupport::Disabled,
|
||||
);
|
||||
client
|
||||
.activate(
|
||||
SessionMeta { user_id: user_id.to_owned(), device_id: "FOOBAR".into() },
|
||||
@@ -1568,8 +1600,10 @@ mod tests {
|
||||
#[async_test]
|
||||
async fn test_ignored_user_list_changes() {
|
||||
let user_id = user_id!("@alice:example.org");
|
||||
let client =
|
||||
BaseClient::new(StoreConfig::new("cross-process-store-locks-holder-name".to_owned()));
|
||||
let client = BaseClient::new(
|
||||
StoreConfig::new("cross-process-store-locks-holder-name".to_owned()),
|
||||
ThreadingSupport::Disabled,
|
||||
);
|
||||
|
||||
client
|
||||
.activate(
|
||||
|
||||
@@ -48,7 +48,7 @@ mod utils;
|
||||
#[cfg(feature = "uniffi")]
|
||||
uniffi::setup_scaffolding!();
|
||||
|
||||
pub use client::BaseClient;
|
||||
pub use client::{BaseClient, ThreadingSupport};
|
||||
#[cfg(any(test, feature = "testing"))]
|
||||
pub use http;
|
||||
#[cfg(feature = "e2e-encryption")]
|
||||
|
||||
@@ -122,7 +122,10 @@ use std::{
|
||||
num::NonZeroUsize,
|
||||
};
|
||||
|
||||
use matrix_sdk_common::{deserialized_responses::TimelineEvent, ring_buffer::RingBuffer};
|
||||
use matrix_sdk_common::{
|
||||
deserialized_responses::TimelineEvent, ring_buffer::RingBuffer,
|
||||
serde_helpers::extract_thread_root,
|
||||
};
|
||||
use ruma::{
|
||||
events::{
|
||||
poll::{start::PollStartEventContent, unstable_start::UnstablePollStartEventContent},
|
||||
@@ -137,6 +140,8 @@ use ruma::{
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tracing::{debug, instrument, trace, warn};
|
||||
|
||||
use crate::ThreadingSupport;
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
struct LatestReadReceipt {
|
||||
/// The id of the event the read receipt is referring to. (Not the read
|
||||
@@ -201,7 +206,18 @@ impl RoomReadReceipts {
|
||||
///
|
||||
/// Returns whether a new event triggered a new unread/notification/mention.
|
||||
#[inline(always)]
|
||||
fn process_event(&mut self, event: &TimelineEvent, user_id: &UserId) {
|
||||
fn process_event(
|
||||
&mut self,
|
||||
event: &TimelineEvent,
|
||||
user_id: &UserId,
|
||||
threading_support: ThreadingSupport,
|
||||
) {
|
||||
if matches!(threading_support, ThreadingSupport::Enabled)
|
||||
&& extract_thread_root(event.raw()).is_some()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if marks_as_unread(event.raw(), user_id) {
|
||||
self.num_unread += 1;
|
||||
}
|
||||
@@ -240,6 +256,7 @@ impl RoomReadReceipts {
|
||||
receipt_event_id: &EventId,
|
||||
user_id: &UserId,
|
||||
events: impl IntoIterator<Item = &'a TimelineEvent>,
|
||||
threading_support: ThreadingSupport,
|
||||
) -> bool {
|
||||
let mut counting_receipts = false;
|
||||
|
||||
@@ -259,7 +276,7 @@ impl RoomReadReceipts {
|
||||
}
|
||||
|
||||
if counting_receipts {
|
||||
self.process_event(event, user_id);
|
||||
self.process_event(event, user_id, threading_support);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -443,6 +460,7 @@ pub(crate) fn compute_unread_counts(
|
||||
mut previous_events: Vec<TimelineEvent>,
|
||||
new_events: &[TimelineEvent],
|
||||
read_receipts: &mut RoomReadReceipts,
|
||||
threading_support: ThreadingSupport,
|
||||
) {
|
||||
debug!(?read_receipts, "Starting");
|
||||
|
||||
@@ -489,7 +507,12 @@ pub(crate) fn compute_unread_counts(
|
||||
|
||||
// The event for the receipt is in `all_events`, so we'll find it and can count
|
||||
// safely from here.
|
||||
read_receipts.find_and_process_events(&event_id, user_id, all_events.iter());
|
||||
read_receipts.find_and_process_events(
|
||||
&event_id,
|
||||
user_id,
|
||||
all_events.iter(),
|
||||
threading_support,
|
||||
);
|
||||
|
||||
debug!(?read_receipts, "after finding a better receipt");
|
||||
return;
|
||||
@@ -503,7 +526,7 @@ pub(crate) fn compute_unread_counts(
|
||||
// for the next receipt.
|
||||
|
||||
for event in new_events {
|
||||
read_receipts.process_event(event, user_id);
|
||||
read_receipts.process_event(event, user_id, threading_support);
|
||||
}
|
||||
|
||||
debug!(?read_receipts, "no better receipt, {} new events", new_events.len());
|
||||
@@ -619,7 +642,10 @@ mod tests {
|
||||
};
|
||||
|
||||
use super::compute_unread_counts;
|
||||
use crate::read_receipts::{marks_as_unread, ReceiptSelector, RoomReadReceipts};
|
||||
use crate::{
|
||||
read_receipts::{marks_as_unread, ReceiptSelector, RoomReadReceipts},
|
||||
ThreadingSupport,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn test_room_message_marks_as_unread() {
|
||||
@@ -720,7 +746,7 @@ mod tests {
|
||||
// An interesting event from oneself doesn't count as a new unread message.
|
||||
let event = make_event(user_id, Vec::new());
|
||||
let mut receipts = RoomReadReceipts::default();
|
||||
receipts.process_event(&event, user_id);
|
||||
receipts.process_event(&event, user_id, ThreadingSupport::Disabled);
|
||||
assert_eq!(receipts.num_unread, 0);
|
||||
assert_eq!(receipts.num_mentions, 0);
|
||||
assert_eq!(receipts.num_notifications, 0);
|
||||
@@ -728,7 +754,7 @@ mod tests {
|
||||
// An interesting event from someone else does count as a new unread message.
|
||||
let event = make_event(user_id!("@bob:example.org"), Vec::new());
|
||||
let mut receipts = RoomReadReceipts::default();
|
||||
receipts.process_event(&event, user_id);
|
||||
receipts.process_event(&event, user_id, ThreadingSupport::Disabled);
|
||||
assert_eq!(receipts.num_unread, 1);
|
||||
assert_eq!(receipts.num_mentions, 0);
|
||||
assert_eq!(receipts.num_notifications, 0);
|
||||
@@ -736,7 +762,7 @@ mod tests {
|
||||
// Push actions computed beforehand are respected.
|
||||
let event = make_event(user_id!("@bob:example.org"), vec![Action::Notify]);
|
||||
let mut receipts = RoomReadReceipts::default();
|
||||
receipts.process_event(&event, user_id);
|
||||
receipts.process_event(&event, user_id, ThreadingSupport::Disabled);
|
||||
assert_eq!(receipts.num_unread, 1);
|
||||
assert_eq!(receipts.num_mentions, 0);
|
||||
assert_eq!(receipts.num_notifications, 1);
|
||||
@@ -746,7 +772,7 @@ mod tests {
|
||||
vec![Action::SetTweak(ruma::push::Tweak::Highlight(true))],
|
||||
);
|
||||
let mut receipts = RoomReadReceipts::default();
|
||||
receipts.process_event(&event, user_id);
|
||||
receipts.process_event(&event, user_id, ThreadingSupport::Disabled);
|
||||
assert_eq!(receipts.num_unread, 1);
|
||||
assert_eq!(receipts.num_mentions, 1);
|
||||
assert_eq!(receipts.num_notifications, 0);
|
||||
@@ -756,7 +782,7 @@ mod tests {
|
||||
vec![Action::SetTweak(ruma::push::Tweak::Highlight(true)), Action::Notify],
|
||||
);
|
||||
let mut receipts = RoomReadReceipts::default();
|
||||
receipts.process_event(&event, user_id);
|
||||
receipts.process_event(&event, user_id, ThreadingSupport::Disabled);
|
||||
assert_eq!(receipts.num_unread, 1);
|
||||
assert_eq!(receipts.num_mentions, 1);
|
||||
assert_eq!(receipts.num_notifications, 1);
|
||||
@@ -765,7 +791,7 @@ mod tests {
|
||||
// make sure to resist against it.
|
||||
let event = make_event(user_id!("@bob:example.org"), vec![Action::Notify, Action::Notify]);
|
||||
let mut receipts = RoomReadReceipts::default();
|
||||
receipts.process_event(&event, user_id);
|
||||
receipts.process_event(&event, user_id, ThreadingSupport::Disabled);
|
||||
assert_eq!(receipts.num_unread, 1);
|
||||
assert_eq!(receipts.num_mentions, 0);
|
||||
assert_eq!(receipts.num_notifications, 1);
|
||||
@@ -779,7 +805,9 @@ mod tests {
|
||||
// When provided with no events, we report not finding the event to which the
|
||||
// receipt relates.
|
||||
let mut receipts = RoomReadReceipts::default();
|
||||
assert!(receipts.find_and_process_events(ev0, user_id, &[]).not());
|
||||
assert!(receipts
|
||||
.find_and_process_events(ev0, user_id, &[], ThreadingSupport::Disabled)
|
||||
.not());
|
||||
assert_eq!(receipts.num_unread, 0);
|
||||
assert_eq!(receipts.num_notifications, 0);
|
||||
assert_eq!(receipts.num_mentions, 0);
|
||||
@@ -801,7 +829,12 @@ mod tests {
|
||||
..Default::default()
|
||||
};
|
||||
assert!(receipts
|
||||
.find_and_process_events(ev0, user_id, &[make_event(event_id!("$1"))],)
|
||||
.find_and_process_events(
|
||||
ev0,
|
||||
user_id,
|
||||
&[make_event(event_id!("$1"))],
|
||||
ThreadingSupport::Disabled
|
||||
)
|
||||
.not());
|
||||
assert_eq!(receipts.num_unread, 42);
|
||||
assert_eq!(receipts.num_notifications, 13);
|
||||
@@ -816,7 +849,12 @@ mod tests {
|
||||
num_mentions: 37,
|
||||
..Default::default()
|
||||
};
|
||||
assert!(receipts.find_and_process_events(ev0, user_id, &[make_event(ev0)]));
|
||||
assert!(receipts.find_and_process_events(
|
||||
ev0,
|
||||
user_id,
|
||||
&[make_event(ev0)],
|
||||
ThreadingSupport::Disabled
|
||||
),);
|
||||
assert_eq!(receipts.num_unread, 0);
|
||||
assert_eq!(receipts.num_notifications, 0);
|
||||
assert_eq!(receipts.num_mentions, 0);
|
||||
@@ -838,6 +876,7 @@ mod tests {
|
||||
make_event(event_id!("$2")),
|
||||
make_event(event_id!("$3"))
|
||||
],
|
||||
ThreadingSupport::Disabled
|
||||
)
|
||||
.not());
|
||||
assert_eq!(receipts.num_unread, 42);
|
||||
@@ -861,6 +900,7 @@ mod tests {
|
||||
make_event(event_id!("$2")),
|
||||
make_event(event_id!("$3"))
|
||||
],
|
||||
ThreadingSupport::Disabled
|
||||
));
|
||||
assert_eq!(receipts.num_unread, 2);
|
||||
assert_eq!(receipts.num_notifications, 0);
|
||||
@@ -883,6 +923,7 @@ mod tests {
|
||||
make_event(event_id!("$2")),
|
||||
make_event(event_id!("$3"))
|
||||
],
|
||||
ThreadingSupport::Disabled
|
||||
));
|
||||
assert_eq!(receipts.num_unread, 2);
|
||||
assert_eq!(receipts.num_notifications, 0);
|
||||
@@ -908,7 +949,7 @@ mod tests {
|
||||
.add(receipt_event_id, user_id, ReceiptType::Read, ReceiptThread::Unthreaded)
|
||||
.into_content();
|
||||
|
||||
let mut read_receipts = Default::default();
|
||||
let mut read_receipts = RoomReadReceipts::default();
|
||||
compute_unread_counts(
|
||||
user_id,
|
||||
room_id,
|
||||
@@ -916,6 +957,7 @@ mod tests {
|
||||
previous_events.clone(),
|
||||
&[ev1.clone(), ev2.clone()],
|
||||
&mut read_receipts,
|
||||
ThreadingSupport::Disabled,
|
||||
);
|
||||
|
||||
// It did find the receipt event (ev1).
|
||||
@@ -934,6 +976,7 @@ mod tests {
|
||||
previous_events,
|
||||
&[new_event],
|
||||
&mut read_receipts,
|
||||
ThreadingSupport::Disabled,
|
||||
);
|
||||
|
||||
// Only the new event should be added.
|
||||
@@ -1000,6 +1043,7 @@ mod tests {
|
||||
all_events.clone(),
|
||||
&[],
|
||||
&mut read_receipts,
|
||||
ThreadingSupport::Disabled,
|
||||
);
|
||||
|
||||
assert!(
|
||||
@@ -1021,6 +1065,7 @@ mod tests {
|
||||
head_events.clone(),
|
||||
&tail_events,
|
||||
&mut read_receipts,
|
||||
ThreadingSupport::Disabled,
|
||||
);
|
||||
|
||||
assert!(
|
||||
@@ -1065,6 +1110,7 @@ mod tests {
|
||||
events,
|
||||
&[], // no new events
|
||||
&mut read_receipts,
|
||||
ThreadingSupport::Disabled,
|
||||
);
|
||||
|
||||
// Then there are no unread events,
|
||||
@@ -1102,6 +1148,7 @@ mod tests {
|
||||
events,
|
||||
&[ev0], // duplicate event!
|
||||
&mut read_receipts,
|
||||
ThreadingSupport::Disabled,
|
||||
);
|
||||
|
||||
// All events are unread, and there's no pending receipt.
|
||||
@@ -1536,6 +1583,7 @@ mod tests {
|
||||
Vec::new(),
|
||||
&events,
|
||||
&mut read_receipts,
|
||||
ThreadingSupport::Disabled,
|
||||
);
|
||||
|
||||
// Only the last two events sent by Bob count as unread.
|
||||
@@ -1547,4 +1595,60 @@ mod tests {
|
||||
// And the active receipt is the implicit one on my event.
|
||||
assert_eq!(read_receipts.latest_active.unwrap().event_id, event_id!("$6"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_compute_unread_counts_with_threading_enabled() {
|
||||
fn make_event(user_id: &UserId, thread_root: &EventId) -> TimelineEvent {
|
||||
EventFactory::new()
|
||||
.text_msg("A")
|
||||
.sender(user_id)
|
||||
.event_id(event_id!("$ida"))
|
||||
.in_thread(thread_root, event_id!("$latest_event"))
|
||||
.into_event()
|
||||
}
|
||||
|
||||
let mut receipts = RoomReadReceipts::default();
|
||||
|
||||
let own_alice = user_id!("@alice:example.org");
|
||||
let bob = user_id!("@bob:example.org");
|
||||
|
||||
// Threaded messages from myself or other users shouldn't change the
|
||||
// unread counts.
|
||||
receipts.process_event(
|
||||
&make_event(own_alice, event_id!("$some_thread_root")),
|
||||
own_alice,
|
||||
ThreadingSupport::Enabled,
|
||||
);
|
||||
receipts.process_event(
|
||||
&make_event(own_alice, event_id!("$some_other_thread_root")),
|
||||
own_alice,
|
||||
ThreadingSupport::Enabled,
|
||||
);
|
||||
|
||||
receipts.process_event(
|
||||
&make_event(bob, event_id!("$some_thread_root")),
|
||||
own_alice,
|
||||
ThreadingSupport::Enabled,
|
||||
);
|
||||
receipts.process_event(
|
||||
&make_event(bob, event_id!("$some_other_thread_root")),
|
||||
own_alice,
|
||||
ThreadingSupport::Enabled,
|
||||
);
|
||||
|
||||
assert_eq!(receipts.num_unread, 0);
|
||||
assert_eq!(receipts.num_mentions, 0);
|
||||
assert_eq!(receipts.num_notifications, 0);
|
||||
|
||||
// Processing an unthreaded message should still count as unread.
|
||||
receipts.process_event(
|
||||
&EventFactory::new().text_msg("A").sender(bob).event_id(event_id!("$ida")).into_event(),
|
||||
own_alice,
|
||||
ThreadingSupport::Enabled,
|
||||
);
|
||||
|
||||
assert_eq!(receipts.num_unread, 1);
|
||||
assert_eq!(receipts.num_mentions, 0);
|
||||
assert_eq!(receipts.num_notifications, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,6 +88,7 @@ mod tests_with_e2e_encryption {
|
||||
use serde_json::json;
|
||||
|
||||
use crate::{
|
||||
client::ThreadingSupport,
|
||||
latest_event::LatestEvent,
|
||||
response_processors as processors,
|
||||
store::{MemoryStore, RoomLoadSettings, StoreConfig},
|
||||
@@ -107,8 +108,10 @@ mod tests_with_e2e_encryption {
|
||||
#[async_test]
|
||||
async fn test_setting_the_latest_event_doesnt_cause_a_room_info_notable_update() {
|
||||
// Given a room,
|
||||
let client =
|
||||
BaseClient::new(StoreConfig::new("cross-process-store-locks-holder-name".to_owned()));
|
||||
let client = BaseClient::new(
|
||||
StoreConfig::new("cross-process-store-locks-holder-name".to_owned()),
|
||||
ThreadingSupport::Disabled,
|
||||
);
|
||||
|
||||
client
|
||||
.activate(
|
||||
|
||||
@@ -1274,7 +1274,7 @@ mod tests {
|
||||
"num_mentions": 0,
|
||||
"num_notifications": 0,
|
||||
"latest_active": null,
|
||||
"pending": []
|
||||
"pending": [],
|
||||
},
|
||||
"recency_stamp": 42,
|
||||
});
|
||||
|
||||
@@ -82,6 +82,7 @@ mod tests {
|
||||
|
||||
use super::{super::BaseRoomInfo, RoomNotableTags};
|
||||
use crate::{
|
||||
client::ThreadingSupport,
|
||||
response_processors as processors,
|
||||
store::{RoomLoadSettings, StoreConfig},
|
||||
BaseClient, RoomState, SessionMeta,
|
||||
@@ -90,8 +91,10 @@ mod tests {
|
||||
#[async_test]
|
||||
async fn test_is_favourite() {
|
||||
// Given a room,
|
||||
let client =
|
||||
BaseClient::new(StoreConfig::new("cross-process-store-locks-holder-name".to_owned()));
|
||||
let client = BaseClient::new(
|
||||
StoreConfig::new("cross-process-store-locks-holder-name".to_owned()),
|
||||
ThreadingSupport::Disabled,
|
||||
);
|
||||
|
||||
client
|
||||
.activate(
|
||||
@@ -186,8 +189,10 @@ mod tests {
|
||||
#[async_test]
|
||||
async fn test_is_low_priority() {
|
||||
// Given a room,
|
||||
let client =
|
||||
BaseClient::new(StoreConfig::new("cross-process-store-locks-holder-name".to_owned()));
|
||||
let client = BaseClient::new(
|
||||
StoreConfig::new("cross-process-store-locks-holder-name".to_owned()),
|
||||
ThreadingSupport::Disabled,
|
||||
);
|
||||
|
||||
client
|
||||
.activate(
|
||||
|
||||
@@ -284,6 +284,7 @@ impl BaseClient {
|
||||
room_previous_events,
|
||||
&joined_room_update.timeline.events,
|
||||
&mut room_info.read_receipts,
|
||||
self.threading_support,
|
||||
);
|
||||
|
||||
if prev_read_receipts != room_info.read_receipts {
|
||||
@@ -348,6 +349,7 @@ mod tests {
|
||||
#[cfg(feature = "e2e-encryption")]
|
||||
use super::processors::room::msc4186::cache_latest_events;
|
||||
use crate::{
|
||||
client::ThreadingSupport,
|
||||
room::{RoomHero, RoomInfoNotableUpdateReasons},
|
||||
store::{RoomLoadSettings, StoreConfig},
|
||||
test_utils::logged_in_base_client,
|
||||
@@ -1157,7 +1159,7 @@ mod tests {
|
||||
let store = StoreConfig::new("cross-process-foo".to_owned());
|
||||
state_store = store.state_store.clone();
|
||||
|
||||
let client = BaseClient::new(store);
|
||||
let client = BaseClient::new(store, ThreadingSupport::Disabled);
|
||||
client
|
||||
.activate(
|
||||
session_meta.clone(),
|
||||
@@ -1188,7 +1190,7 @@ mod tests {
|
||||
let client = {
|
||||
let mut store = StoreConfig::new("cross-process-foo".to_owned());
|
||||
store.state_store = state_store;
|
||||
let client = BaseClient::new(store);
|
||||
let client = BaseClient::new(store, ThreadingSupport::Disabled);
|
||||
client
|
||||
.activate(
|
||||
session_meta,
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
use ruma::{owned_user_id, UserId};
|
||||
|
||||
use crate::{
|
||||
client::ThreadingSupport,
|
||||
store::{RoomLoadSettings, StoreConfig},
|
||||
BaseClient, SessionMeta,
|
||||
};
|
||||
@@ -26,8 +27,10 @@ use crate::{
|
||||
/// Create a [`BaseClient`] with the given user id, if provided, or an hardcoded
|
||||
/// one otherwise.
|
||||
pub(crate) async fn logged_in_base_client(user_id: Option<&UserId>) -> BaseClient {
|
||||
let client =
|
||||
BaseClient::new(StoreConfig::new("cross-process-store-locks-holder-name".to_owned()));
|
||||
let client = BaseClient::new(
|
||||
StoreConfig::new("cross-process-store-locks-holder-name".to_owned()),
|
||||
ThreadingSupport::Disabled,
|
||||
);
|
||||
let user_id =
|
||||
user_id.map(|user_id| user_id.to_owned()).unwrap_or_else(|| owned_user_id!("@u:e.uk"));
|
||||
client
|
||||
|
||||
@@ -22,7 +22,7 @@ use std::{fmt, sync::Arc};
|
||||
use homeserver_config::*;
|
||||
#[cfg(feature = "e2e-encryption")]
|
||||
use matrix_sdk_base::crypto::DecryptionSettings;
|
||||
use matrix_sdk_base::{store::StoreConfig, BaseClient};
|
||||
use matrix_sdk_base::{store::StoreConfig, BaseClient, ThreadingSupport};
|
||||
#[cfg(feature = "sqlite")]
|
||||
use matrix_sdk_sqlite::SqliteStoreConfig;
|
||||
use ruma::{
|
||||
@@ -113,6 +113,7 @@ pub struct ClientBuilder {
|
||||
#[cfg(feature = "e2e-encryption")]
|
||||
enable_share_history_on_invite: bool,
|
||||
cross_process_store_locks_holder_name: String,
|
||||
threading_support: ThreadingSupport,
|
||||
}
|
||||
|
||||
impl ClientBuilder {
|
||||
@@ -143,6 +144,7 @@ impl ClientBuilder {
|
||||
enable_share_history_on_invite: false,
|
||||
cross_process_store_locks_holder_name:
|
||||
Self::DEFAULT_CROSS_PROCESS_STORE_LOCKS_HOLDER_NAME.to_owned(),
|
||||
threading_support: ThreadingSupport::Disabled,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -479,6 +481,14 @@ impl ClientBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
/// Whether the threads feature is enabled throuoghout the SDK.
|
||||
/// This will affect how timelines are setup, how read receipts are sent
|
||||
/// and how room unreads are computed.
|
||||
pub fn with_threading_support(mut self, threading_support: ThreadingSupport) -> Self {
|
||||
self.threading_support = threading_support;
|
||||
self
|
||||
}
|
||||
|
||||
/// Create a [`Client`] with the options set on this builder.
|
||||
///
|
||||
/// # Errors
|
||||
@@ -515,6 +525,7 @@ impl ClientBuilder {
|
||||
let mut client = BaseClient::new(
|
||||
build_store_config(self.store_config, &self.cross_process_store_locks_holder_name)
|
||||
.await?,
|
||||
self.threading_support,
|
||||
);
|
||||
|
||||
#[cfg(feature = "e2e-encryption")]
|
||||
|
||||
@@ -28,7 +28,7 @@ pub use matrix_sdk_base::{
|
||||
ComposerDraft, ComposerDraftType, EncryptionState, PredecessorRoom, QueueWedgeError,
|
||||
Room as BaseRoom, RoomCreateWithCreatorEventContent, RoomDisplayName, RoomHero, RoomInfo,
|
||||
RoomMember as BaseRoomMember, RoomMemberships, RoomState, SessionMeta, StateChanges,
|
||||
StateStore, StoreError, SuccessorRoom,
|
||||
StateStore, StoreError, SuccessorRoom, ThreadingSupport,
|
||||
};
|
||||
pub use matrix_sdk_common::*;
|
||||
pub use reqwest;
|
||||
|
||||
Reference in New Issue
Block a user