Receive support for editing messages

This commit is contained in:
Josh Perez 2023-03-27 19:48:57 -04:00 committed by GitHub
parent 2781e621ad
commit 36e21c0134
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
46 changed files with 2053 additions and 405 deletions

View file

@ -387,6 +387,13 @@ export type FTSOptimizationStateType = Readonly<{
done?: boolean;
}>;
export type EditedMessageType = Readonly<{
fromId: string;
messageId: string;
sentAt: number;
readStatus: MessageType['readStatus'];
}>;
export type DataInterface = {
close: () => Promise<void>;
removeDB: () => Promise<void>;
@ -514,6 +521,10 @@ export type DataInterface = {
readAt?: number;
storyId?: string;
}) => Promise<GetUnreadByConversationAndMarkReadResultType>;
getUnreadEditedMessagesAndMarkRead: (options: {
fromId: string;
newestUnreadAt: number;
}) => Promise<GetUnreadByConversationAndMarkReadResultType>;
getUnreadReactionsAndMarkRead: (options: {
conversationId: string;
newestUnreadAt: number;
@ -543,9 +554,15 @@ export type DataInterface = {
messageIds: ReadonlyArray<string>
) => Promise<Array<MessageType>>;
_getAllMessages: () => Promise<Array<MessageType>>;
_getAllEditedMessages: () => Promise<
Array<{ messageId: string; sentAt: number }>
>;
_removeAllMessages: () => Promise<void>;
getAllMessageIds: () => Promise<Array<string>>;
getMessagesBySentAt: (sentAt: number) => Promise<Array<MessageType>>;
getMessagesIncludingEditedBySentAt: (
sentAt: number
) => Promise<Array<MessageType>>;
getExpiredMessages: () => Promise<Array<MessageType>>;
getMessagesUnexpectedlyMissingExpirationStartTimestamp: () => Promise<
Array<MessageType>
@ -592,6 +609,11 @@ export type DataInterface = {
getNearbyMessageFromDeletedSet: (
options: GetNearbyMessageFromDeletedSetOptionsType
) => Promise<string | null>;
saveEditedMessage: (
mainMessage: MessageType,
ourUuid: UUIDStringType,
opts: EditedMessageType
) => Promise<void>;
getUnprocessedCount: () => Promise<number>;
getUnprocessedByIdsAndIncrementAttempts: (
ids: ReadonlyArray<string>

View file

@ -87,6 +87,7 @@ import type {
ConversationType,
DeleteSentProtoRecipientOptionsType,
DeleteSentProtoRecipientResultType,
EditedMessageType,
EmojiType,
FTSOptimizationStateType,
GetAllStoriesResultType,
@ -252,9 +253,12 @@ const dataInterface: ServerInterface = {
getMessageById,
getMessagesById,
_getAllMessages,
_getAllEditedMessages,
_removeAllMessages,
getAllMessageIds,
getMessagesBySentAt,
getMessagesIncludingEditedBySentAt,
getUnreadEditedMessagesAndMarkRead,
getExpiredMessages,
getMessagesUnexpectedlyMissingExpirationStartTimestamp,
getSoonestMessageExpiry,
@ -273,6 +277,7 @@ const dataInterface: ServerInterface = {
migrateConversationMessages,
getMessagesBetween,
getNearbyMessageFromDeletedSet,
saveEditedMessage,
getUnprocessedCount,
getUnprocessedByIdsAndIncrementAttempts,
@ -5679,3 +5684,136 @@ async function removeAllProfileKeyCredentials(): Promise<void> {
`
);
}
async function saveEditedMessage(
mainMessage: MessageType,
ourUuid: UUIDStringType,
{ fromId, messageId, readStatus, sentAt }: EditedMessageType
): Promise<void> {
const db = getInstance();
db.transaction(() => {
assertSync(
saveMessageSync(mainMessage, {
ourUuid,
alreadyInTransaction: true,
})
);
const [query, params] = sql`
INSERT INTO edited_messages (
fromId,
messageId,
sentAt,
readStatus
) VALUES (
${fromId},
${messageId},
${sentAt},
${readStatus}
);
`;
db.prepare(query).run(params);
})();
}
async function getMessagesIncludingEditedBySentAt(
sentAt: number
): Promise<Array<MessageType>> {
const db = getInstance();
const [query, params] = sql`
SELECT messages.json, received_at, sent_at FROM edited_messages
INNER JOIN messages ON
messages.id = edited_messages.messageId
WHERE edited_messages.sentAt = ${sentAt}
UNION
SELECT json, received_at, sent_at FROM messages
WHERE sent_at = ${sentAt}
ORDER BY messages.received_at DESC, messages.sent_at DESC;
`;
const rows = db.prepare(query).all(params);
return rows.map(row => jsonToObject(row.json));
}
async function _getAllEditedMessages(): Promise<
Array<{ messageId: string; sentAt: number }>
> {
const db = getInstance();
return db
.prepare<Query>(
`
SELECT * FROM edited_messages;
`
)
.all({});
}
async function getUnreadEditedMessagesAndMarkRead({
fromId,
newestUnreadAt,
}: {
fromId: string;
newestUnreadAt: number;
}): Promise<GetUnreadByConversationAndMarkReadResultType> {
const db = getInstance();
return db.transaction(() => {
const [selectQuery, selectParams] = sql`
SELECT
messages.id,
messages.json,
edited_messages.sentAt,
edited_messages.readStatus
FROM edited_messages
JOIN messages
ON messages.id = edited_messages.messageId
WHERE
edited_messages.readStatus = ${ReadStatus.Unread} AND
edited_messages.fromId = ${fromId} AND
received_at <= ${newestUnreadAt}
ORDER BY messages.received_at DESC, messages.sent_at DESC;
`;
const rows = db.prepare(selectQuery).all(selectParams);
if (rows.length) {
const newestSentAt = rows[0].sentAt;
const [updateStatusQuery, updateStatusParams] = sql`
UPDATE edited_messages
SET
readStatus = ${ReadStatus.Read}
WHERE
readStatus = ${ReadStatus.Unread} AND
fromId = ${fromId} AND
sentAt <= ${newestSentAt};
`;
db.prepare(updateStatusQuery).run(updateStatusParams);
}
return rows.map(row => {
const json = jsonToObject<MessageType>(row.json);
return {
originalReadStatus: row.readStatus,
readStatus: ReadStatus.Read,
seenStatus: SeenStatus.Seen,
...pick(json, [
'expirationStartTimestamp',
'id',
'sent_at',
'source',
'sourceUuid',
'type',
]),
// Use the edited message timestamp
sent_at: row.sentAt,
};
});
})();
}

View file

@ -0,0 +1,34 @@
// Copyright 2023 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import type { Database } from '@signalapp/better-sqlite3';
import type { LoggerType } from '../../types/Logging';
export default function updateToSchemaVersion80(
currentVersion: number,
db: Database,
logger: LoggerType
): void {
if (currentVersion >= 80) {
return;
}
db.transaction(() => {
db.exec(`
CREATE TABLE edited_messages(
fromId STRING,
messageId STRING REFERENCES messages(id)
ON DELETE CASCADE,
sentAt INTEGER,
readStatus INTEGER
);
CREATE INDEX edited_messages_sent_at ON edited_messages (sentAt);
`);
db.pragma('user_version = 80');
})();
logger.info('updateToSchemaVersion80: success!');
}

View file

@ -55,6 +55,7 @@ import updateToSchemaVersion76 from './76-optimize-convo-open-2';
import updateToSchemaVersion77 from './77-signal-tokenizer';
import updateToSchemaVersion78 from './78-merge-receipt-jobs';
import updateToSchemaVersion79 from './79-paging-lightbox';
import updateToSchemaVersion80 from './80-edited-messages';
function updateToSchemaVersion1(
currentVersion: number,
@ -1979,6 +1980,8 @@ export const SCHEMA_VERSIONS = [
updateToSchemaVersion77,
updateToSchemaVersion78,
updateToSchemaVersion79,
updateToSchemaVersion80,
];
export function updateSchema(db: Database, logger: LoggerType): void {