diff --git a/keyserver/src/endpoints.js b/keyserver/src/endpoints.js --- a/keyserver/src/endpoints.js +++ b/keyserver/src/endpoints.js @@ -31,6 +31,7 @@ reactionMessageCreationResponder, editMessageCreationResponder, fetchPinnedMessagesResponder, + searchMessagesResponder, } from './responders/message-responders.js'; import { updateRelationshipsResponder } from './responders/relationship-responders.js'; import { @@ -200,6 +201,10 @@ responder: entryRestorationResponder, requiredPolicies: baseLegalPolicies, }, + search_messages: { + responder: searchMessagesResponder, + requiredPolicies: baseLegalPolicies, + }, search_users: { responder: userSearchResponder, requiredPolicies: baseLegalPolicies, diff --git a/keyserver/src/fetchers/message-fetchers.js b/keyserver/src/fetchers/message-fetchers.js --- a/keyserver/src/fetchers/message-fetchers.js +++ b/keyserver/src/fetchers/message-fetchers.js @@ -27,6 +27,7 @@ type FetchPinnedMessagesRequest, type FetchPinnedMessagesResult, isMessageSidebarSourceReactionOrEdit, + type SearchMessagesResponse, } from 'lib/types/message-types.js'; import { defaultNumberPerThread } from 'lib/types/message-types.js'; import { threadPermissions } from 'lib/types/thread-permission-types.js'; @@ -904,17 +905,17 @@ return [...rawMessageInfos, ...rawRelatedMessageInfos]; } -const searchMessagesPageSize: number = defaultNumberPerThread + 1; +const searchMessagesPageSize = defaultNumberPerThread + 1; async function searchMessagesInSingleChat( inputQuery: string, threadID: string, viewer?: Viewer, cursor?: string, -): Promise<$ReadOnlyArray> { +): Promise { if (inputQuery === '') { console.warn('received empty search query'); - return []; + return { messages: [], endReached: true }; } const pattern = processQueryForSearch(inputQuery); @@ -944,18 +945,25 @@ const [results] = await dbQuery(query); if (results.length === 0) { - return []; + return { messages: [], endReached: true }; } + const endReached = results.length < searchMessagesPageSize; + + const resultsPage = endReached ? results : results.slice(0, -1); + const rawMessageInfos = await rawMessageInfoForRowsAndRelatedMessages( - results, + resultsPage, viewer, ); - return shimUnsupportedRawMessageInfos( - rawMessageInfos, - viewer?.platformDetails, - ); + return { + messages: shimUnsupportedRawMessageInfos( + rawMessageInfos, + viewer?.platformDetails, + ), + endReached: endReached, + }; } export { @@ -972,5 +980,4 @@ fetchRelatedMessages, rawMessageInfoForRowsAndRelatedMessages, searchMessagesInSingleChat, - searchMessagesPageSize, }; diff --git a/keyserver/src/responders/message-responders.js b/keyserver/src/responders/message-responders.js --- a/keyserver/src/responders/message-responders.js +++ b/keyserver/src/responders/message-responders.js @@ -25,6 +25,8 @@ type FetchPinnedMessagesResult, messageTruncationStatusesValidator, rawMessageInfoValidator, + type SearchMessagesResponse, + type SearchMessagesRequest, } from 'lib/types/message-types.js'; import type { EditMessageData } from 'lib/types/messages/edit.js'; import type { ReactionMessageData } from 'lib/types/messages/reaction.js'; @@ -48,6 +50,7 @@ fetchMessageInfoByID, fetchThreadMessagesCount, fetchPinnedMessageInfos, + searchMessagesInSingleChat, } from '../fetchers/message-fetchers.js'; import { fetchServerThreadInfos } from '../fetchers/thread-fetchers.js'; import { checkThreadPermission } from '../fetchers/thread-permission-fetchers.js'; @@ -476,6 +479,27 @@ ); } +const searchMessagesResponderInputValidator = tShape({ + query: t.String, + threadID: t.String, + cursor: t.maybe(t.String), +}); + +async function searchMessagesResponder( + viewer: Viewer, + input: any, +): Promise { + const request: SearchMessagesRequest = input; + await validateInput(viewer, searchMessagesResponderInputValidator, input); + + return await searchMessagesInSingleChat( + request.query, + request.threadID, + viewer, + request.cursor, + ); +} + export { textMessageCreationResponder, messageFetchResponder, @@ -483,4 +507,5 @@ reactionMessageCreationResponder, editMessageCreationResponder, fetchPinnedMessagesResponder, + searchMessagesResponder, }; diff --git a/lib/types/endpoints.js b/lib/types/endpoints.js --- a/lib/types/endpoints.js +++ b/lib/types/endpoints.js @@ -91,6 +91,7 @@ SIWE_AUTH: 'siwe_auth', UPDATE_USER_AVATAR: 'update_user_avatar', UPLOAD_MEDIA_METADATA: 'upload_media_metadata', + SEARCH_MESSAGES: 'search_messages', }); type SocketPreferredEndpoint = $Values; diff --git a/lib/types/message-types.js b/lib/types/message-types.js --- a/lib/types/message-types.js +++ b/lib/types/message-types.js @@ -717,3 +717,14 @@ export type FetchPinnedMessagesResult = { +pinnedMessages: $ReadOnlyArray, }; + +export type SearchMessagesRequest = { + +query: string, + +threadID: string, + +cursor?: string, +}; + +export type SearchMessagesResponse = { + +messages: $ReadOnlyArray, + +endReached: boolean, +};