Changeset View
Changeset View
Standalone View
Standalone View
keyserver/src/fetchers/message-fetchers.js
Show All 9 Lines | |||||
import { getNotifCollapseKey } from 'lib/shared/notif-utils.js'; | import { getNotifCollapseKey } from 'lib/shared/notif-utils.js'; | ||||
import { hasMinCodeVersion } from 'lib/shared/version-utils.js'; | import { hasMinCodeVersion } from 'lib/shared/version-utils.js'; | ||||
import { | import { | ||||
type RawMessageInfo, | type RawMessageInfo, | ||||
type RawComposableMessageInfo, | type RawComposableMessageInfo, | ||||
type RawRobotextMessageInfo, | type RawRobotextMessageInfo, | ||||
messageTypes, | messageTypes, | ||||
type MessageType, | type MessageType, | ||||
type EditMessageContent, | |||||
assertMessageType, | assertMessageType, | ||||
type MessageSelectionCriteria, | type MessageSelectionCriteria, | ||||
type MessageTruncationStatus, | type MessageTruncationStatus, | ||||
messageTruncationStatus, | messageTruncationStatus, | ||||
type FetchMessageInfosResult, | type FetchMessageInfosResult, | ||||
defaultMaxMessageAge, | defaultMaxMessageAge, | ||||
} from 'lib/types/message-types.js'; | } from 'lib/types/message-types.js'; | ||||
import { threadPermissions } from 'lib/types/thread-types.js'; | import { threadPermissions } from 'lib/types/thread-types.js'; | ||||
▲ Show 20 Lines • Show All 614 Lines • ▼ Show 20 Lines | if (result.length === 0) { | ||||
return null; | return null; | ||||
} | } | ||||
const derivedMessages = await fetchDerivedMessages(result, viewer); | const derivedMessages = await fetchDerivedMessages(result, viewer); | ||||
return rawMessageInfoFromRows(result, viewer, derivedMessages); | return rawMessageInfoFromRows(result, viewer, derivedMessages); | ||||
} | } | ||||
async function fetchMessageRowsByIDs(messageIDs: $ReadOnlyArray<string>) { | async function fetchMessageRowsByIDs(messageIDs: $ReadOnlyArray<string>) { | ||||
const query = SQL` | const query = SQL` | ||||
SELECT m.id, m.thread AS threadID, m.content, m.time, m.type, m.creation, | SELECT m.id, m.thread AS threadID, m.content, m.time, m.type, m.creation, | ||||
m.user AS creatorID, m.target_message as targetMessageID, | m.user AS creatorID, m.target_message as targetMessageID, | ||||
stm.permissions AS subthread_permissions, up.id AS uploadID, | stm.permissions AS subthread_permissions, up.id AS uploadID, | ||||
up.type AS uploadType, up.secret AS uploadSecret, up.extra AS uploadExtra | up.type AS uploadType, up.secret AS uploadSecret, up.extra AS uploadExtra | ||||
FROM messages m | FROM messages m | ||||
LEFT JOIN uploads up ON up.container = m.id | LEFT JOIN uploads up ON up.container = m.id | ||||
LEFT JOIN memberships stm ON m.type = ${messageTypes.CREATE_SUB_THREAD} | LEFT JOIN memberships stm ON m.type = ${messageTypes.CREATE_SUB_THREAD} | ||||
AND stm.thread = m.content AND stm.user = m.user | AND stm.thread = m.content AND stm.user = m.user | ||||
WHERE m.id IN (${messageIDs}) | WHERE m.id IN (${messageIDs}) | ||||
Show All 19 Lines | > { | ||||
const messagesByID = new Map< | const messagesByID = new Map< | ||||
string, | string, | ||||
RawComposableMessageInfo | RawRobotextMessageInfo, | RawComposableMessageInfo | RawRobotextMessageInfo, | ||||
>(); | >(); | ||||
if (requiredIDs.size === 0) { | if (requiredIDs.size === 0) { | ||||
return messagesByID; | return messagesByID; | ||||
} | } | ||||
const result = await fetchMessageRowsByIDs([...requiredIDs]); | const [result, edits] = await Promise.all([ | ||||
fetchMessageRowsByIDs([...requiredIDs]), | |||||
fetchLatestEditMessageContentByIDs([...requiredIDs]), | |||||
]); | |||||
const messages = await parseMessageSQLResult(result, new Map(), viewer); | const messages = await parseMessageSQLResult(result, new Map(), viewer); | ||||
for (const message of messages) { | for (const message of messages) { | ||||
const { rawMessageInfo } = message; | let { rawMessageInfo } = message; | ||||
if (rawMessageInfo.id) { | if (rawMessageInfo.id) { | ||||
invariant( | invariant( | ||||
rawMessageInfo.type !== messageTypes.SIDEBAR_SOURCE && | rawMessageInfo.type !== messageTypes.SIDEBAR_SOURCE && | ||||
rawMessageInfo.type !== messageTypes.REACTION && | rawMessageInfo.type !== messageTypes.REACTION && | ||||
rawMessageInfo.type !== messageTypes.EDIT_MESSAGE, | rawMessageInfo.type !== messageTypes.EDIT_MESSAGE, | ||||
'SIDEBAR_SOURCE should not point to a SIDEBAR_SOURCE, REACTION or EDIT_MESSAGE', | 'SIDEBAR_SOURCE should not point to a SIDEBAR_SOURCE, REACTION or EDIT_MESSAGE', | ||||
); | ); | ||||
const editedContent = edits.get(rawMessageInfo.id); | |||||
if (editedContent && rawMessageInfo.type === messageTypes.TEXT) { | |||||
rawMessageInfo = { | |||||
...rawMessageInfo, | |||||
text: editedContent.text, | |||||
}; | |||||
} | |||||
invariant(rawMessageInfo.id, 'rawMessageInfo.id should not be null'); | |||||
messagesByID.set(rawMessageInfo.id, rawMessageInfo); | messagesByID.set(rawMessageInfo.id, rawMessageInfo); | ||||
} | } | ||||
} | } | ||||
return messagesByID; | return messagesByID; | ||||
} | } | ||||
async function fetchMessageInfoByID( | async function fetchMessageInfoByID( | ||||
viewer?: Viewer, | viewer?: Viewer, | ||||
Show All 12 Lines | const query = SQL` | ||||
SELECT COUNT(*) AS count | SELECT COUNT(*) AS count | ||||
FROM messages | FROM messages | ||||
WHERE thread = ${threadID} | WHERE thread = ${threadID} | ||||
`; | `; | ||||
const [result] = await dbQuery(query); | const [result] = await dbQuery(query); | ||||
return result[0].count; | return result[0].count; | ||||
} | } | ||||
async function fetchLatestEditMessageContentByIDs( | |||||
messageIDs: $ReadOnlyArray<string>, | |||||
): Promise<$ReadOnlyMap<string, EditMessageContent>> { | |||||
const latestEditedMessageQuery = SQL` | |||||
SELECT m.id, ( | |||||
SELECT m2.content | |||||
FROM messages m2 | |||||
WHERE m.id = m2.target_message | |||||
AND m.thread = m2.thread | |||||
AND m2.type = ${messageTypes.EDIT_MESSAGE} | |||||
ORDER BY time DESC, id DESC | |||||
LIMIT 1 | |||||
) content | |||||
FROM messages m | |||||
WHERE m.id IN(${messageIDs}) | |||||
`; | |||||
const [result] = await dbQuery(latestEditedMessageQuery); | |||||
const latestContentByID = new Map(); | |||||
for (const row of result) { | |||||
if (!row.content) { | |||||
continue; | |||||
} | |||||
const content = JSON.parse(row.content); | |||||
latestContentByID.set(row.id.toString(), content); | |||||
} | |||||
return latestContentByID; | |||||
} | |||||
async function fetchLatestEditMessageContentByID( | |||||
messageID: string, | |||||
): Promise<?EditMessageContent> { | |||||
const result = await fetchLatestEditMessageContentByIDs([messageID]); | |||||
const content = result.get(messageID); | |||||
return content; | |||||
} | |||||
export { | export { | ||||
fetchCollapsableNotifs, | fetchCollapsableNotifs, | ||||
fetchMessageInfos, | fetchMessageInfos, | ||||
fetchMessageInfosSince, | fetchMessageInfosSince, | ||||
getMessageFetchResultFromRedisMessages, | getMessageFetchResultFromRedisMessages, | ||||
fetchMessageInfoForLocalID, | fetchMessageInfoForLocalID, | ||||
fetchMessageInfoForEntryAction, | fetchMessageInfoForEntryAction, | ||||
fetchMessageInfoByID, | fetchMessageInfoByID, | ||||
fetchThreadMessagesCount, | fetchThreadMessagesCount, | ||||
fetchLatestEditMessageContentByID, | |||||
}; | }; |