Receive support for editing messages
This commit is contained in:
parent
2781e621ad
commit
36e21c0134
46 changed files with 2053 additions and 405 deletions
|
@ -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>
|
||||
|
|
138
ts/sql/Server.ts
138
ts/sql/Server.ts
|
@ -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,
|
||||
};
|
||||
});
|
||||
})();
|
||||
}
|
||||
|
|
34
ts/sql/migrations/80-edited-messages.ts
Normal file
34
ts/sql/migrations/80-edited-messages.ts
Normal 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!');
|
||||
}
|
|
@ -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 {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue