diff --git a/crates/prose-core-client/src/domain/rooms/models/room.rs b/crates/prose-core-client/src/domain/rooms/models/room.rs index d66e13ba..5a9cc06c 100644 --- a/crates/prose-core-client/src/domain/rooms/models/room.rs +++ b/crates/prose-core-client/src/domain/rooms/models/room.rs @@ -20,7 +20,7 @@ use crate::domain::rooms::models::{ use crate::domain::settings::models::SyncedRoomSettings; use crate::domain::shared::models::{AccountId, Availability, RoomId, RoomType, UserId}; use crate::domain::sidebar::models::Bookmark; -use crate::dtos::OccupantId; +use crate::dtos::{OccupantId, ParticipantId}; /// Contains information about a connected room and its state. #[derive(Debug, Clone)] @@ -234,17 +234,28 @@ impl Room { .get_messages_after(account, &self.room_id, last_read_message_timestamp) .await?; + let our_participant_id = self + .occupant_id() + .map(ParticipantId::Occupant) + .unwrap_or_else(|| ParticipantId::User(account.to_user_id())); + for message in messages { - if let MessageLikePayload::Message { ref mentions, .. } = message.payload { - for mention in mentions { - if account == &mention.user { - stats.mentions_count += 1; - break; - } - } + let MessageLikePayload::Message { ref mentions, .. } = message.payload else { + continue; + }; + + if message.from == our_participant_id { + continue; + } - stats.unread_count += 1; + for mention in mentions { + if account == &mention.user { + stats.mentions_count += 1; + break; + } } + + stats.unread_count += 1; } self.inner.details.write().statistics = stats.clone(); diff --git a/tests/prose-core-integration-tests/src/tests/client/catchup_unread.rs b/tests/prose-core-integration-tests/src/tests/client/catchup_unread.rs index b1c43314..cae62e5c 100644 --- a/tests/prose-core-integration-tests/src/tests/client/catchup_unread.rs +++ b/tests/prose-core-integration-tests/src/tests/client/catchup_unread.rs @@ -108,6 +108,86 @@ async fn test_maintains_message_count_from_prior_runs() -> Result<()> { Ok(()) } +#[mt_test] +async fn test_does_not_count_sent_messages_in_muc_as_unread() -> Result<()> { + let client = TestClient::new().await; + + client + .expect_login(user_id!("user@prose.org"), "secret") + .await?; + + let room_id = muc_id!("room@conf.prose.org"); + let our_occupant_id = client.build_occupant_id(&room_id); + + client + .join_room_with_strategy( + room_id, + "anon-id", + JoinRoomStrategy::default().with_catch_up_handler(move |client, room_id| { + client.expect_muc_catchup_with_config( + room_id, + client.time_provider.now() + - Duration::seconds(client.app_config.max_catchup_duration_secs), + vec![ + MessageBuilder::new_with_index(1) + .set_from(occupant_id!("room@conf.prose.org/friend")) + .build_archived_message("", None), + MessageBuilder::new_with_index(2) + .set_from(our_occupant_id.clone()) + .build_archived_message("", None), + MessageBuilder::new_with_index(3) + .set_from(occupant_id!("room@conf.prose.org/friend")) + .build_archived_message("", None), + ], + ); + }), + ) + .await?; + + let sidebar_items = client.sidebar.sidebar_items().await; + assert_eq!(1, sidebar_items.len()); + assert_eq!(*&sidebar_items[0].unread_count, 2); + + Ok(()) +} + +#[mt_test] +async fn test_does_not_count_sent_messages_in_dm_as_unread() -> Result<()> { + let client = TestClient::new().await; + + client + .expect_login(user_id!("user@prose.org"), "secret") + .await?; + + client + .start_dm_with_strategy( + user_id!("other@prose.org"), + StartDMStrategy::default().with_catch_up_handler(|client, user_id| { + client.expect_catchup_with_config( + user_id, + vec![ + MessageBuilder::new_with_index(1) + .set_from(user_id!("other@prose.org")) + .build_archived_message("", None), + MessageBuilder::new_with_index(2) + .set_from(user_id!("user@prose.org")) + .build_archived_message("", None), + MessageBuilder::new_with_index(3) + .set_from(user_id!("other@prose.org")) + .build_archived_message("", None), + ], + ) + }), + ) + .await?; + + let sidebar_items = client.sidebar.sidebar_items().await; + assert_eq!(1, sidebar_items.len()); + assert_eq!(*&sidebar_items[0].unread_count, 2); + + Ok(()) +} + #[mt_test] async fn test_loads_unread_messages() -> Result<()> { let store = store().await.expect("Failed to set up store."); diff --git a/tests/prose-core-integration-tests/src/tests/client/helpers/test_client_muc.rs b/tests/prose-core-integration-tests/src/tests/client/helpers/test_client_muc.rs index 16b6e9cc..baafe328 100644 --- a/tests/prose-core-integration-tests/src/tests/client/helpers/test_client_muc.rs +++ b/tests/prose-core-integration-tests/src/tests/client/helpers/test_client_muc.rs @@ -27,20 +27,49 @@ pub struct JoinRoomStrategy { pub expect_catchup: Box, } -#[derive(Default)] +impl Default for JoinRoomStrategy { + fn default() -> Self { + JoinRoomStrategy { + room_settings: None, + expect_catchup: Box::new(|client, room_id| client.expect_muc_catchup(room_id)), + } + } +} + +impl JoinRoomStrategy { + pub fn with_catch_up_handler( + mut self, + handler: impl FnOnce(&TestClient, &MucId) + 'static, + ) -> Self { + self.expect_catchup = Box::new(handler); + self + } +} + pub struct StartDMStrategy { pub room_settings: Option, + pub expect_catchup: Box, } -impl Default for JoinRoomStrategy { +impl Default for StartDMStrategy { fn default() -> Self { - JoinRoomStrategy { + Self { room_settings: None, - expect_catchup: Box::new(|client, room_id| client.expect_muc_catchup(room_id)), + expect_catchup: Box::new(|client, user_id| client.expect_catchup(user_id)), } } } +impl StartDMStrategy { + pub fn with_catch_up_handler( + mut self, + handler: impl FnOnce(&TestClient, &UserId) + 'static, + ) -> Self { + self.expect_catchup = Box::new(handler); + self + } +} + impl TestClient { pub fn build_occupant_id(&self, room_id: &MucId) -> OccupantId { let nickname = build_nickname( @@ -332,7 +361,7 @@ impl TestClient { ); self.expect_load_settings(user_id.clone(), strategy.room_settings); - self.expect_catchup(&user_id); + (strategy.expect_catchup)(&self, &user_id); self.expect_set_bookmark( &RoomId::User(user_id.clone()), @@ -605,6 +634,14 @@ impl TestClient { } pub fn expect_catchup(&self, room_id: &UserId) { + self.expect_catchup_with_config(room_id, None); + } + + pub fn expect_catchup_with_config( + &self, + room_id: &UserId, + messages: impl IntoIterator, + ) { let start = self.time_provider.now() - Duration::seconds(self.app_config.max_catchup_duration_secs); @@ -640,6 +677,15 @@ impl TestClient { "# ); + let query_id = QueryId(self.id_provider.id_with_offset(1)); + + for mut archived_message in messages.into_iter() { + archived_message.query_id = Some(query_id.clone()); + + let message = Message::new().set_archived_message(archived_message); + self.receive_element(Element::from(message), file!(), line!()); + } + recv!( self, r#"