Skip to content

Commit

Permalink
feat(room): add 'seen request to join ids' to the stores
Browse files Browse the repository at this point in the history
This will allow us to keep track of which join room requests are marked as 'seen' by the current user and return them as such.

Also, add some methods to `Room` to mark new join requests as seen and to get the current ids for the seen join requests.
  • Loading branch information
jmartinesp committed Dec 10, 2024
1 parent d5e7a9c commit df46f24
Show file tree
Hide file tree
Showing 6 changed files with 134 additions and 5 deletions.
4 changes: 4 additions & 0 deletions crates/matrix-sdk-base/src/rooms/normal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,9 @@ pub struct Room {
/// to disk but held in memory.
#[cfg(all(feature = "e2e-encryption", feature = "experimental-sliding-sync"))]
pub latest_encrypted_events: Arc<SyncRwLock<RingBuffer<Raw<AnySyncTimelineEvent>>>>,

/// The event ids for seen request to join room events.
pub seen_join_request_ids: SharedObservable<Option<HashSet<OwnedEventId>>>,
}

/// The room summary containing member counts and members that should be used to
Expand Down Expand Up @@ -272,6 +275,7 @@ impl Room {
Self::MAX_ENCRYPTED_EVENTS,
))),
room_info_notable_update_sender,
seen_join_request_ids: SharedObservable::new(None),
}
}

Expand Down
21 changes: 20 additions & 1 deletion crates/matrix-sdk-base/src/store/memory_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
// limitations under the License.

use std::{
collections::{BTreeMap, BTreeSet, HashMap},
collections::{BTreeMap, BTreeSet, HashMap, HashSet},
sync::RwLock as StdRwLock,
};

Expand Down Expand Up @@ -91,6 +91,7 @@ pub struct MemoryStore {
custom: StdRwLock<HashMap<Vec<u8>, Vec<u8>>>,
send_queue_events: StdRwLock<BTreeMap<OwnedRoomId, Vec<QueuedRequest>>>,
dependent_send_queue_events: StdRwLock<BTreeMap<OwnedRoomId, Vec<DependentQueuedRequest>>>,
seen_requests_to_join: StdRwLock<BTreeMap<OwnedRoomId, HashSet<OwnedEventId>>>,
}

impl MemoryStore {
Expand Down Expand Up @@ -186,6 +187,13 @@ impl StateStore for MemoryStore {
.get(room_id)
.cloned()
.map(StateStoreDataValue::ComposerDraft),
StateStoreDataKey::SeenJoinRequests(room_id) => self
.seen_requests_to_join
.read()
.unwrap()
.get(room_id)
.cloned()
.map(StateStoreDataValue::SeenJoinRequests),
})
}

Expand Down Expand Up @@ -239,6 +247,14 @@ impl StateStore for MemoryStore {
.expect("Session data not containing server capabilities"),
);
}
StateStoreDataKey::SeenJoinRequests(room_id) => {
self.seen_requests_to_join.write().unwrap().insert(
room_id.to_owned(),
value
.into_seen_join_requests()
.expect("Session data is not a set of seen join request ids"),
);
}
}

Ok(())
Expand All @@ -265,6 +281,9 @@ impl StateStore for MemoryStore {
StateStoreDataKey::ComposerDraft(room_id) => {
self.composer_drafts.write().unwrap().remove(room_id);
}
StateStoreDataKey::SeenJoinRequests(room_id) => {
self.seen_requests_to_join.write().unwrap().remove(room_id);
}
}
Ok(())
}
Expand Down
17 changes: 16 additions & 1 deletion crates/matrix-sdk-base/src/store/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

use std::{
borrow::Borrow,
collections::{BTreeMap, BTreeSet, HashMap},
collections::{BTreeMap, BTreeSet, HashMap, HashSet},
fmt,
sync::Arc,
};
Expand Down Expand Up @@ -1022,6 +1022,9 @@ pub enum StateStoreDataValue {
///
/// [`ComposerDraft`]: Self::ComposerDraft
ComposerDraft(ComposerDraft),

/// A list of requests to join marked as seen in a room.
SeenJoinRequests(HashSet<OwnedEventId>),
}

/// Current draft of the composer for the room.
Expand Down Expand Up @@ -1088,6 +1091,11 @@ impl StateStoreDataValue {
pub fn into_server_capabilities(self) -> Option<ServerCapabilities> {
as_variant!(self, Self::ServerCapabilities)
}

/// Get this value if it is the data for the ignored join requests.
pub fn into_seen_join_requests(self) -> Option<HashSet<OwnedEventId>> {
as_variant!(self, Self::SeenJoinRequests)
}
}

/// A key for key-value data.
Expand Down Expand Up @@ -1117,6 +1125,9 @@ pub enum StateStoreDataKey<'a> {
///
/// [`ComposerDraft`]: Self::ComposerDraft
ComposerDraft(&'a RoomId),

/// A list of requests to join in a room marked as seen.
SeenJoinRequests(&'a RoomId),
}

impl StateStoreDataKey<'_> {
Expand All @@ -1142,6 +1153,10 @@ impl StateStoreDataKey<'_> {
/// Key prefix to use for the [`ComposerDraft`][Self::ComposerDraft]
/// variant.
pub const COMPOSER_DRAFT: &'static str = "composer_draft";

/// Key prefix to use for the
/// [`SeenJoinRequests`][Self::SeenJoinRequests] variant.
pub const SEEN_REQUESTS_TO_JOIN: &'static str = "seen_requests_to_join";
}

#[cfg(test)]
Expand Down
12 changes: 12 additions & 0 deletions crates/matrix-sdk-indexeddb/src/state_store/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,9 @@ impl IndexeddbStateStore {
StateStoreDataKey::ComposerDraft(room_id) => {
self.encode_key(keys::KV, (StateStoreDataKey::COMPOSER_DRAFT, room_id))
}
StateStoreDataKey::SeenJoinRequests(room_id) => {
self.encode_key(keys::KV, (StateStoreDataKey::SEEN_REQUESTS_TO_JOIN, room_id))
}
}
}
}
Expand Down Expand Up @@ -537,6 +540,10 @@ impl_state_store!({
.map(|f| self.deserialize_value::<ComposerDraft>(&f))
.transpose()?
.map(StateStoreDataValue::ComposerDraft),
StateStoreDataKey::SeenJoinRequests(_) => value
.map(|f| self.deserialize_value::<HashSet<OwnedEventId>>(&f))
.transpose()?
.map(StateStoreDataValue::SeenJoinRequests),
};

Ok(value)
Expand Down Expand Up @@ -574,6 +581,11 @@ impl_state_store!({
StateStoreDataKey::ComposerDraft(_) => self.serialize_value(
&value.into_composer_draft().expect("Session data not a composer draft"),
),
StateStoreDataKey::SeenJoinRequests(_) => self.serialize_value(
&value
.into_seen_join_requests()
.expect("Session data is not a set of seen join request ids"),
),
};

let tx =
Expand Down
11 changes: 11 additions & 0 deletions crates/matrix-sdk-sqlite/src/state_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,9 @@ impl SqliteStateStore {
StateStoreDataKey::ComposerDraft(room_id) => {
Cow::Owned(format!("{}:{room_id}", StateStoreDataKey::COMPOSER_DRAFT))
}
StateStoreDataKey::SeenJoinRequests(room_id) => {
Cow::Owned(format!("{}:{room_id}", StateStoreDataKey::SEEN_REQUESTS_TO_JOIN))
}
};

self.encode_key(keys::KV_BLOB, &*key_s)
Expand Down Expand Up @@ -995,6 +998,9 @@ impl StateStore for SqliteStateStore {
StateStoreDataKey::ComposerDraft(_) => {
StateStoreDataValue::ComposerDraft(self.deserialize_value(&data)?)
}
StateStoreDataKey::SeenJoinRequests(_) => {
StateStoreDataValue::SeenJoinRequests(self.deserialize_value(&data)?)
}
})
})
.transpose()
Expand Down Expand Up @@ -1029,6 +1035,11 @@ impl StateStore for SqliteStateStore {
StateStoreDataKey::ComposerDraft(_) => self.serialize_value(
&value.into_composer_draft().expect("Session data not a composer draft"),
)?,
StateStoreDataKey::SeenJoinRequests(_) => self.serialize_value(
&value
.into_seen_join_requests()
.expect("Session data is not a set of seen join request ids"),
)?,
};

self.acquire()
Expand Down
74 changes: 71 additions & 3 deletions crates/matrix-sdk/src/room/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
use std::{
borrow::Borrow,
collections::{BTreeMap, HashMap},
collections::{BTreeMap, HashMap, HashSet},
ops::Deref,
sync::Arc,
time::Duration,
Expand Down Expand Up @@ -3192,6 +3192,48 @@ impl Room {
pub fn observe_live_location_shares(&self) -> ObservableLiveLocation {
ObservableLiveLocation::new(&self.client, self.room_id())
}

/// Mark a list of requests to join the room as seen, given their state
/// event ids.
pub async fn mark_join_requests_as_seen(&self, event_ids: &[OwnedEventId]) -> Result<()> {
let mut current_seen_events = self.get_seen_join_request_ids().await?;

for event_id in event_ids {
current_seen_events.insert(event_id.to_owned());
}

self.seen_join_request_ids.set(Some(current_seen_events.clone()));

self.client
.store()
.set_kv_data(
StateStoreDataKey::SeenJoinRequests(self.room_id()),
StateStoreDataValue::SeenJoinRequests(current_seen_events),
)
.await
.map_err(Into::into)
}

/// Get the list of seen requests to join event ids in this room.
pub async fn get_seen_join_request_ids(&self) -> Result<HashSet<OwnedEventId>> {
let current_join_request_ids = self.seen_join_request_ids.get();
let current_join_request_ids: HashSet<OwnedEventId> =
if let Some(requests) = current_join_request_ids.as_ref() {
requests.clone()
} else {
let requests = self
.client
.store()
.get_kv_data(StateStoreDataKey::SeenJoinRequests(self.room_id()))
.await?
.and_then(|v| v.into_seen_join_requests())
.unwrap_or_default();

self.seen_join_request_ids.set(Some(requests.clone()));
requests
};
Ok(current_join_request_ids)
}
}

#[cfg(all(feature = "e2e-encryption", not(target_arch = "wasm32")))]
Expand Down Expand Up @@ -3482,8 +3524,9 @@ mod tests {
use matrix_sdk_base::{store::ComposerDraftType, ComposerDraft, SessionMeta};
use matrix_sdk_test::{
async_test, test_json, JoinedRoomBuilder, StateTestEvent, SyncResponseBuilder,
DEFAULT_TEST_ROOM_ID,
};
use ruma::{device_id, int, user_id};
use ruma::{device_id, event_id, int, user_id};
use wiremock::{
matchers::{header, method, path_regex},
Mock, MockServer, ResponseTemplate,
Expand All @@ -3493,7 +3536,7 @@ mod tests {
use crate::{
config::RequestConfig,
matrix_auth::{MatrixSession, MatrixSessionTokens},
test_utils::logged_in_client,
test_utils::{logged_in_client, mocks::MatrixMockServer},
Client,
};

Expand Down Expand Up @@ -3668,4 +3711,29 @@ mod tests {
room.clear_composer_draft().await.unwrap();
assert_eq!(room.load_composer_draft().await.unwrap(), None);
}

#[async_test]
async fn test_mark_join_requests_as_seen() {
let server = MatrixMockServer::new().await;
let client = server.client_builder().build().await;
let event_id = event_id!("$a:b.c");

let room = server.sync_joined_room(&client, &DEFAULT_TEST_ROOM_ID).await;

// When loading the initial seen ids, there are none
let seen_ids =
room.get_seen_join_request_ids().await.expect("Couldn't load seen join request ids");
assert!(seen_ids.is_empty());

// We mark a random event id as seen
room.mark_join_requests_as_seen(&[event_id.to_owned()])
.await
.expect("Couldn't mark join request as seen");

// Then we can check it was successfully marked as seen
let seen_ids =
room.get_seen_join_request_ids().await.expect("Couldn't load seen join request ids");
assert_eq!(seen_ids.len(), 1);
assert_eq!(seen_ids.into_iter().next().expect("No next value"), event_id)
}
}

0 comments on commit df46f24

Please sign in to comment.