Skip to content

Commit

Permalink
Rename JoinRequest in the SDK crates to KnockRequest, make `Room:…
Browse files Browse the repository at this point in the history
…:mark_knock_requests_as_seen` thread safe and pass `user_ids` instead of `event_ids`: the user ids will be used to get the related member state events and they'll only be marked as seen if they're in a knock state.

Also, add extra checks to the integration tests.
  • Loading branch information
jmartinesp committed Dec 16, 2024
1 parent 3387695 commit 05d46e6
Show file tree
Hide file tree
Showing 9 changed files with 309 additions and 247 deletions.
46 changes: 23 additions & 23 deletions bindings/matrix-sdk-ffi/src/room.rs
Original file line number Diff line number Diff line change
Expand Up @@ -912,17 +912,17 @@ impl Room {
Ok(())
}

/// Subscribes to requests to join this room, using a `listener` to be
/// notified of the changes.
/// Subscribes to requests to join this room (knock member events), using a
/// `listener` to be notified of the changes.
///
/// The current requests to join the room will be emitted immediately
/// when subscribing, along with a [`TaskHandle`] to cancel the
/// subscription.
pub async fn subscribe_to_join_requests(
pub async fn subscribe_to_knock_requests(
self: Arc<Self>,
listener: Box<dyn JoinRequestsListener>,
listener: Box<dyn KnockRequestsListener>,
) -> Result<Arc<TaskHandle>, ClientError> {
let stream = self.inner.subscribe_to_join_requests().await?;
let stream = self.inner.subscribe_to_knock_requests().await?;

let handle = Arc::new(TaskHandle::new(RUNTIME.spawn(async move {
pin_mut!(stream);
Expand All @@ -935,8 +935,8 @@ impl Room {
}
}

impl From<matrix_sdk::room::request_to_join::JoinRequest> for JoinRequest {
fn from(request: matrix_sdk::room::request_to_join::JoinRequest) -> Self {
impl From<matrix_sdk::room::knock_requests::KnockRequest> for KnockRequest {
fn from(request: matrix_sdk::room::knock_requests::KnockRequest) -> Self {
Self {
event_id: request.event_id.to_string(),
user_id: request.member_info.user_id.to_string(),
Expand All @@ -946,20 +946,20 @@ impl From<matrix_sdk::room::request_to_join::JoinRequest> for JoinRequest {
reason: request.member_info.reason.clone(),
timestamp: request.timestamp.map(|ts| ts.into()),
is_seen: request.is_seen,
actions: Arc::new(JoinRequestActions { inner: request }),
actions: Arc::new(KnockRequestActions { inner: request }),
}
}
}

/// A listener for receiving new requests to a join a room.
#[matrix_sdk_ffi_macros::export(callback_interface)]
pub trait JoinRequestsListener: Send + Sync {
fn call(&self, join_requests: Vec<JoinRequest>);
pub trait KnockRequestsListener: Send + Sync {
fn call(&self, join_requests: Vec<KnockRequest>);
}

/// An FFI representation of a request to join a room.
#[derive(Debug, Clone, uniffi::Record)]
pub struct JoinRequest {
pub struct KnockRequest {
/// The event id of the event that contains the `knock` membership change.
pub event_id: String,
/// The user id of the user who's requesting to join the room.
Expand All @@ -974,44 +974,44 @@ pub struct JoinRequest {
pub reason: Option<String>,
/// The timestamp when this request was created.
pub timestamp: Option<u64>,
/// Whether the request to join has been marked as `seen` so it can be
/// Whether the knock request has been marked as `seen` so it can be
/// filtered by the client.
pub is_seen: bool,
/// A set of actions to perform for this request to join.
pub actions: Arc<JoinRequestActions>,
/// A set of actions to perform for this knock request.
pub actions: Arc<KnockRequestActions>,
}

/// A set of actions to perform for a request to join.
/// A set of actions to perform for a knock request.
#[derive(Debug, Clone, uniffi::Object)]
pub struct JoinRequestActions {
inner: matrix_sdk::room::request_to_join::JoinRequest,
pub struct KnockRequestActions {
inner: matrix_sdk::room::knock_requests::KnockRequest,
}

#[matrix_sdk_ffi_macros::export]
impl JoinRequestActions {
/// Accepts the request to join by inviting the user to the room.
impl KnockRequestActions {
/// Accepts the knock request by inviting the user to the room.
pub async fn accept(&self) -> Result<(), ClientError> {
self.inner.accept().await.map_err(Into::into)
}

/// Declines the request to join by kicking the user from the room with an
/// Declines the knock request by kicking the user from the room with an
/// optional reason.
pub async fn decline(&self, reason: Option<String>) -> Result<(), ClientError> {
self.inner.decline(reason.as_deref()).await.map_err(Into::into)
}

/// Declines the request to join by banning the user from the room with an
/// Declines the knock request by banning the user from the room with an
/// optional reason.
pub async fn decline_and_ban(&self, reason: Option<String>) -> Result<(), ClientError> {
self.inner.decline_and_ban(reason.as_deref()).await.map_err(Into::into)
}

/// Marks the request as 'seen'.
/// Marks the knock request as 'seen'.
///
/// **IMPORTANT**: this won't update the current reference to this request,
/// a new one with the updated value should be emitted instead.
pub async fn mark_as_seen(&self) -> Result<(), ClientError> {
self.inner.clone().mark_as_seen().await.map_err(Into::into)
self.inner.mark_as_seen().await.map_err(Into::into)
}
}

Expand Down
82 changes: 82 additions & 0 deletions crates/matrix-sdk-base/src/rooms/normal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1177,6 +1177,88 @@ impl Room {
pub fn pinned_event_ids(&self) -> Option<Vec<OwnedEventId>> {
self.inner.read().pinned_event_ids()
}

/// Mark a list of requests to join the room as seen, given their state
/// event ids.
pub async fn mark_knock_requests_as_seen(&self, user_ids: &[OwnedUserId]) -> StoreResult<()> {
let raw_user_ids: Vec<&str> = user_ids.iter().map(|id| id.as_str()).collect();
let member_raw_events = self
.store
.get_state_events_for_keys(self.room_id(), StateEventType::RoomMember, &raw_user_ids)
.await?;
let mut event_to_user_ids = Vec::with_capacity(member_raw_events.len());

// Map the list of events ids to their user ids, if they are event ids for knock
// membership events. Log an error and continue otherwise.
for raw_event in member_raw_events {
let event = raw_event.cast::<RoomMemberEventContent>().deserialize()?;
match event {
SyncOrStrippedState::Sync(SyncStateEvent::Original(event)) => {
if event.content.membership == MembershipState::Knock {
event_to_user_ids.push((event.event_id, event.state_key))
} else {
warn!("Could not mark knock event as seen: event {} for user {} is not in Knock membership state.", event.event_id, event.state_key);
}
}
_ => warn!(
"Could not mark knock event as seen: event for user {} is not valid.",
event.state_key()
),
}
}

let mut current_seen_events_guard = self.seen_knock_request_ids_map.write().await;
// We're not calling `get_seen_join_request_ids` here because we need to keep
// the Mutex's guard until we've updated the data
let mut current_seen_events = if current_seen_events_guard.is_none() {
self.load_cached_knock_request_ids().await?
} else {
current_seen_events_guard.clone().unwrap()
};

current_seen_events.extend(event_to_user_ids);

ObservableWriteGuard::set(
&mut current_seen_events_guard,
Some(current_seen_events.clone()),
);

self.store
.set_kv_data(
StateStoreDataKey::SeenKnockRequests(self.room_id()),
StateStoreDataValue::SeenKnockRequests(current_seen_events),
)
.await?;

Ok(())
}

/// Get the list of seen knock request event ids in this room.
pub async fn get_seen_knock_request_ids(
&self,
) -> Result<BTreeMap<OwnedEventId, OwnedUserId>, StoreError> {
let mut guard = self.seen_knock_request_ids_map.write().await;
if guard.is_none() {
ObservableWriteGuard::set(
&mut guard,
Some(self.load_cached_knock_request_ids().await?),
);
}
Ok(guard.clone().unwrap_or_default())
}

/// This loads the current list of seen knock request ids from the state
/// store.
async fn load_cached_knock_request_ids(
&self,
) -> StoreResult<BTreeMap<OwnedEventId, OwnedUserId>> {
Ok(self
.store
.get_kv_data(StateStoreDataKey::SeenKnockRequests(self.room_id()))
.await?
.and_then(|v| v.into_seen_knock_requests())
.unwrap_or_default())
}
}

// See https://github.com/matrix-org/matrix-rust-sdk/pull/3749#issuecomment-2312939823.
Expand Down
2 changes: 1 addition & 1 deletion crates/matrix-sdk-base/src/store/memory_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ impl StateStore for MemoryStore {
inner.seen_knock_requests.insert(
room_id.to_owned(),
value
.into_seen_join_requests()
.into_seen_knock_requests()
.expect("Session data is not a set of seen join request ids"),
);
}
Expand Down
4 changes: 2 additions & 2 deletions crates/matrix-sdk-base/src/store/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1093,7 +1093,7 @@ impl StateStoreDataValue {
}

/// Get this value if it is the data for the ignored join requests.
pub fn into_seen_join_requests(self) -> Option<BTreeMap<OwnedEventId, OwnedUserId>> {
pub fn into_seen_knock_requests(self) -> Option<BTreeMap<OwnedEventId, OwnedUserId>> {
as_variant!(self, Self::SeenKnockRequests)
}
}
Expand Down Expand Up @@ -1126,7 +1126,7 @@ pub enum StateStoreDataKey<'a> {
/// [`ComposerDraft`]: Self::ComposerDraft
ComposerDraft(&'a RoomId),

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

Expand Down
4 changes: 2 additions & 2 deletions crates/matrix-sdk-indexeddb/src/state_store/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -583,8 +583,8 @@ impl_state_store!({
),
StateStoreDataKey::SeenKnockRequests(_) => self.serialize_value(
&value
.into_seen_join_requests()
.expect("Session data is not a set of seen join request ids"),
.into_seen_knock_requests()
.expect("Session data is not a set of seen knock request ids"),
),
};

Expand Down
4 changes: 2 additions & 2 deletions crates/matrix-sdk-sqlite/src/state_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1037,8 +1037,8 @@ impl StateStore for SqliteStateStore {
)?,
StateStoreDataKey::SeenKnockRequests(_) => self.serialize_value(
&value
.into_seen_join_requests()
.expect("Session data is not a set of seen join request ids"),
.into_seen_knock_requests()
.expect("Session data is not a set of seen knock request ids"),
)?,
};

Expand Down
Loading

0 comments on commit 05d46e6

Please sign in to comment.