chore(ui): rename SearchService types and the module itself

This commit is contained in:
Stefan Ceriu
2026-07-01 09:35:35 +03:00
committed by Stefan Ceriu
parent 159fdd096e
commit 6dbc400fc8
4 changed files with 66 additions and 64 deletions

View File

@@ -26,7 +26,7 @@ mod room_preview;
mod ruma;
mod runtime;
#[cfg(feature = "experimental-search")]
mod search;
mod search_service;
mod session_verification;
mod spaces;
mod store;

View File

@@ -17,9 +17,9 @@ use std::{fmt::Debug, sync::Arc};
use eyeball_im::VectorDiff;
use futures_util::{StreamExt as _, pin_mut};
use matrix_sdk_common::{SendOutsideWasm, SyncOutsideWasm};
use matrix_sdk_ui::search::{
SearchService as UISearchService, SearchServiceMessageResult as UISearchServiceMessageResult,
SearchServicePaginationState, SearchServiceResult as UISearchServiceResult,
use matrix_sdk_ui::search_service::{
MessageResult as UIMessageResult, PaginationState as UIPaginationState,
ResultType as UIResultType, SearchService as UISearchService,
};
use crate::{
@@ -67,7 +67,7 @@ impl SearchService {
/// Returns the current pagination state.
pub fn pagination_state(&self) -> SearchServicePaginationState {
self.inner.pagination_state()
self.inner.pagination_state().into()
}
/// Subscribe to pagination state updates.
@@ -81,7 +81,7 @@ impl SearchService {
pin_mut!(pagination_state);
while let Some(state) = pagination_state.next().await {
listener.on_update(state);
listener.on_update(state.into());
}
})))
}
@@ -105,6 +105,25 @@ impl SearchService {
}
}
/// Whether the search service is currently loading a page of results.
#[derive(uniffi::Enum)]
pub enum SearchServicePaginationState {
/// Not currently paginating. `end_reached` is `true` once every source has
/// been exhausted for the current query.
Idle { end_reached: bool },
/// A page of results is currently being loaded.
Loading,
}
impl From<UIPaginationState> for SearchServicePaginationState {
fn from(state: UIPaginationState) -> Self {
match state {
UIPaginationState::Idle { end_reached } => Self::Idle { end_reached },
UIPaginationState::Loading => Self::Loading,
}
}
}
#[matrix_sdk_ffi_macros::export(callback_interface)]
pub trait SearchServicePaginationStateListener: SendOutsideWasm + SyncOutsideWasm + Debug {
fn on_update(&self, pagination_state: SearchServicePaginationState);
@@ -122,10 +141,10 @@ pub enum SearchServiceResult {
Message { room_id: String, result: MessageSearchResult },
}
impl From<UISearchServiceResult> for SearchServiceResult {
fn from(result: UISearchServiceResult) -> Self {
impl From<UIResultType> for SearchServiceResult {
fn from(result: UIResultType) -> Self {
match result {
UISearchServiceResult::Message(message) => {
UIResultType::Message(message) => {
let room_id = message.room_id.to_string();
Self::Message { room_id, result: message.into() }
}
@@ -143,8 +162,8 @@ pub struct MessageSearchResult {
timestamp: Timestamp,
}
impl From<UISearchServiceMessageResult> for MessageSearchResult {
fn from(result: UISearchServiceMessageResult) -> Self {
impl From<UIMessageResult> for MessageSearchResult {
fn from(result: UIMessageResult) -> Self {
Self {
event_id: result.event_id.to_string(),
sender: result.sender.to_string(),
@@ -170,8 +189,8 @@ pub enum SearchServiceResultsUpdate {
Reset { values: Vec<SearchServiceResult> },
}
impl From<VectorDiff<UISearchServiceResult>> for SearchServiceResultsUpdate {
fn from(diff: VectorDiff<UISearchServiceResult>) -> Self {
impl From<VectorDiff<UIResultType>> for SearchServiceResultsUpdate {
fn from(diff: VectorDiff<UIResultType>) -> Self {
match diff {
VectorDiff::Append { values } => {
Self::Append { values: values.into_iter().map(Into::into).collect() }

View File

@@ -22,7 +22,7 @@ pub mod encryption_sync_service;
pub mod notification_client;
pub mod room_list_service;
#[cfg(feature = "experimental-search")]
pub mod search;
pub mod search_service;
pub mod spaces;
pub mod sync_service;
pub mod timeline;

View File

@@ -15,13 +15,13 @@
//! A global, reactive search service.
//!
//! [`SearchService`] aggregates results of different kinds into a single
//! reactive, paginated list of typed [`SearchServiceResult`]s. Call
//! reactive, paginated list of typed [`ResultType`]s. Call
//! [`SearchService::set_query`] to start (or restart) a search, then drive it
//! page by page with [`SearchService::paginate`], observing the results and the
//! [`SearchServicePaginationState`] as they change.
//! [`PaginationState`] as they change.
//!
//! Today the only source is the SDK's per-room message search; people, rooms
//! and other kinds are expected to be added as further [`SearchServiceResult`]
//! and other kinds are expected to be added as further [`ResultType`]
//! variants, and a `matrix-sdk` source can be swapped for a server-side one
//! without changing this interface.
@@ -44,9 +44,8 @@ type ResultsStream =
Pin<Box<dyn Stream<Item = Result<Vec<(OwnedRoomId, TimelineEvent)>, SearchError>> + Send>>;
/// Whether the search service is currently loading a page of results.
#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum SearchServicePaginationState {
pub enum PaginationState {
/// Not currently paginating. `end_reached` is `true` once every source has
/// been exhausted for the current query.
Idle { end_reached: bool },
@@ -58,14 +57,14 @@ pub enum SearchServicePaginationState {
///
/// New result kinds will be added as additional variants.
#[derive(Debug, Clone)]
pub enum SearchServiceResult {
pub enum ResultType {
/// A message (room timeline event) matching the query.
Message(SearchServiceMessageResult),
Message(MessageResult),
}
/// A message matching a search query, with its content and sender resolved.
#[derive(Debug, Clone)]
pub struct SearchServiceMessageResult {
pub struct MessageResult {
/// The room the message belongs to.
pub room_id: OwnedRoomId,
/// The event ID of the message.
@@ -80,7 +79,7 @@ pub struct SearchServiceMessageResult {
pub timestamp: MilliSecondsSinceUnixEpoch,
}
impl SearchServiceMessageResult {
impl MessageResult {
/// Resolve a search hit into a full result by loading its content and
/// sender profile, returning `None` if the event can't be rendered.
async fn from_event(room: &Room, event: TimelineEvent) -> Option<Self> {
@@ -109,9 +108,9 @@ pub struct SearchService {
stream: AsyncMutex<Option<ResultsStream>>,
pagination_state: SharedObservable<SearchServicePaginationState>,
pagination_state: SharedObservable<PaginationState>,
results: AsyncMutex<ObservableVector<SearchServiceResult>>,
results: AsyncMutex<ObservableVector<ResultType>>,
}
impl SearchService {
@@ -120,9 +119,7 @@ impl SearchService {
Self {
client,
stream: AsyncMutex::new(None),
pagination_state: SharedObservable::new(SearchServicePaginationState::Idle {
end_reached: false,
}),
pagination_state: SharedObservable::new(PaginationState::Idle { end_reached: false }),
results: AsyncMutex::new(ObservableVector::new()),
}
}
@@ -134,32 +131,30 @@ impl SearchService {
let stream = self.client.search_messages(query).build_events();
*self.stream.lock().await = Some(Box::pin(stream));
self.results.lock().await.clear();
self.pagination_state.set(SearchServicePaginationState::Idle { end_reached: false });
self.pagination_state.set(PaginationState::Idle { end_reached: false });
self.paginate().await
}
/// Returns the current pagination state.
pub fn pagination_state(&self) -> SearchServicePaginationState {
pub fn pagination_state(&self) -> PaginationState {
self.pagination_state.get()
}
/// Subscribe to pagination state updates.
pub fn subscribe_to_pagination_state_updates(
&self,
) -> Subscriber<SearchServicePaginationState> {
pub fn subscribe_to_pagination_state_updates(&self) -> Subscriber<PaginationState> {
self.pagination_state.subscribe()
}
/// Return the current list of results.
pub async fn results(&self) -> Vec<SearchServiceResult> {
pub async fn results(&self) -> Vec<ResultType> {
self.results.lock().await.iter().cloned().collect()
}
/// Subscribe to result list updates.
pub async fn subscribe_to_results(
&self,
) -> (Vector<SearchServiceResult>, VectorSubscriberBatchedStream<SearchServiceResult>) {
) -> (Vector<ResultType>, VectorSubscriberBatchedStream<ResultType>) {
self.results.lock().await.subscribe().into_values_and_batched_stream()
}
@@ -169,27 +164,26 @@ impl SearchService {
let mut pagination_state = self.pagination_state.write();
match *pagination_state {
SearchServicePaginationState::Idle { end_reached } if end_reached => return Ok(()),
SearchServicePaginationState::Loading => return Ok(()),
PaginationState::Idle { end_reached } if end_reached => return Ok(()),
PaginationState::Loading => return Ok(()),
_ => {}
}
ObservableWriteGuard::set(&mut pagination_state, SearchServicePaginationState::Loading);
ObservableWriteGuard::set(&mut pagination_state, PaginationState::Loading);
}
let mut stream = self.stream.lock().await;
let Some(stream) = stream.as_mut() else {
self.pagination_state.set(SearchServicePaginationState::Idle { end_reached: true });
self.pagination_state.set(PaginationState::Idle { end_reached: true });
return Ok(());
};
match stream.next().await {
None => {
self.pagination_state.set(SearchServicePaginationState::Idle { end_reached: true });
self.pagination_state.set(PaginationState::Idle { end_reached: true });
}
Some(Err(err)) => {
self.pagination_state
.set(SearchServicePaginationState::Idle { end_reached: false });
self.pagination_state.set(PaginationState::Idle { end_reached: false });
return Err(err);
}
Some(Ok(page)) => {
@@ -198,19 +192,17 @@ impl SearchService {
let Some(room) = self.client.get_room(&room_id) else {
continue;
};
let Some(result) = SearchServiceMessageResult::from_event(&room, event).await
else {
let Some(result) = MessageResult::from_event(&room, event).await else {
continue;
};
resolved.push_back(SearchServiceResult::Message(result));
resolved.push_back(ResultType::Message(result));
}
if !resolved.is_empty() {
self.results.lock().await.append(resolved);
}
self.pagination_state
.set(SearchServicePaginationState::Idle { end_reached: false });
self.pagination_state.set(PaginationState::Idle { end_reached: false });
}
}
@@ -231,7 +223,7 @@ mod tests {
use stream_assert::{assert_next_matches, assert_pending};
use tokio::time::sleep;
use super::{SearchService, SearchServicePaginationState, SearchServiceResult};
use super::{PaginationState, ResultType, SearchService};
#[async_test]
async fn test_search_pagination() {
@@ -260,22 +252,16 @@ mod tests {
let search = SearchService::new(client);
// Starts idle and empty.
assert_eq!(
search.pagination_state(),
SearchServicePaginationState::Idle { end_reached: false }
);
assert_eq!(search.pagination_state(), PaginationState::Idle { end_reached: false });
assert!(search.results().await.is_empty());
// Setting the query loads the first page automatically.
search.set_query("world".to_owned()).await.unwrap();
assert_eq!(
search.pagination_state(),
SearchServicePaginationState::Idle { end_reached: false }
);
assert_eq!(search.pagination_state(), PaginationState::Idle { end_reached: false });
let results = search.results().await;
assert_eq!(results.len(), 1);
assert_let!(SearchServiceResult::Message(message) = &results[0]);
assert_let!(ResultType::Message(message) = &results[0]);
assert_eq!(message.event_id, event_id);
// Subscribing now yields the loaded results as the current state.
@@ -288,10 +274,7 @@ mod tests {
search.paginate().await.unwrap();
assert_pending!(results_stream);
assert_eq!(
search.pagination_state(),
SearchServicePaginationState::Idle { end_reached: true }
);
assert_eq!(search.pagination_state(), PaginationState::Idle { end_reached: true });
assert_eq!(search.results().await.len(), 1);
}
@@ -332,7 +315,7 @@ mod tests {
let (initial, results_stream) = search.subscribe_to_results().await;
assert_eq!(initial.len(), 1);
assert_let!(SearchServiceResult::Message(message) = &initial[0]);
assert_let!(ResultType::Message(message) = &initial[0]);
assert_eq!(message.event_id, apple_event);
pin_mut!(results_stream);
assert_pending!(results_stream);
@@ -344,13 +327,13 @@ mod tests {
assert_next_matches!(results_stream, diffs => {
assert_let!([VectorDiff::Clear, VectorDiff::Append { values }] = diffs.as_slice());
assert_eq!(values.len(), 1);
assert_let!(SearchServiceResult::Message(message) = &values[0]);
assert_let!(ResultType::Message(message) = &values[0]);
assert_eq!(message.event_id, banana_event);
});
let results = search.results().await;
assert_eq!(results.len(), 1);
assert_let!(SearchServiceResult::Message(message) = &results[0]);
assert_let!(ResultType::Message(message) = &results[0]);
assert_eq!(message.event_id, banana_event);
}
}