diff --git a/keyserver/src/endpoints.js b/keyserver/src/endpoints.js --- a/keyserver/src/endpoints.js +++ b/keyserver/src/endpoints.js @@ -28,6 +28,7 @@ reactionMessageCreationResponder, editMessageCreationResponder, fetchPinnedMessagesResponder, + searchMessagesResponder, } from './responders/message-responders.js'; import { updateRelationshipsResponder } from './responders/relationship-responders.js'; import { @@ -183,6 +184,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 @@ -25,6 +25,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-types.js'; @@ -908,17 +909,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); @@ -948,18 +949,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 { @@ -976,5 +984,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 @@ -23,6 +23,8 @@ type SendEditMessageResponse, type FetchPinnedMessagesRequest, type FetchPinnedMessagesResult, + 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'; @@ -44,6 +46,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'; @@ -401,6 +404,27 @@ return await fetchPinnedMessageInfos(viewer, request); } +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, @@ -408,4 +432,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 @@ -88,6 +88,7 @@ SIWE_NONCE: 'siwe_nonce', SIWE_AUTH: 'siwe_auth', UPDATE_USER_AVATAR: 'update_user_avatar', + 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 @@ -666,3 +666,14 @@ export type FetchPinnedMessagesResult = { +pinnedMessages: $ReadOnlyArray, }; + +export type SearchMessagesRequest = { + +query: string, + +threadID: string, + +cursor?: string, +}; + +export type SearchMessagesResponse = { + +messages: $ReadOnlyArray, + +endReached: boolean, +};