From c332bd240f5461bf5c01dfc6dd5991ab8773ab3e Mon Sep 17 00:00:00 2001 From: ayumi-signal <143036029+ayumi-signal@users.noreply.github.com> Date: Mon, 11 Mar 2024 11:18:55 -0700 Subject: [PATCH] Handle new sync message MarkedAsRead for Calls Tab --- protos/SignalService.proto | 1 + ts/sql/Interface.ts | 4 ++- ts/sql/Server.ts | 5 +++- ts/state/ducks/callHistory.ts | 20 +++++---------- ts/textsecure/MessageReceiver.ts | 4 +++ ts/types/CallDisposition.ts | 1 + ts/util/callDisposition.ts | 43 ++++++++++++++++++++++++++++++++ ts/util/onCallLogEventSync.ts | 10 ++++++++ 8 files changed, 72 insertions(+), 16 deletions(-) diff --git a/protos/SignalService.proto b/protos/SignalService.proto index c3aeb0c148b..3859b3ecdb3 100644 --- a/protos/SignalService.proto +++ b/protos/SignalService.proto @@ -614,6 +614,7 @@ message SyncMessage { message CallLogEvent { enum Type { CLEAR = 0; + MARKED_AS_READ = 1; } optional Type type = 1; diff --git a/ts/sql/Interface.ts b/ts/sql/Interface.ts index 61506122b77..879f047477f 100644 --- a/ts/sql/Interface.ts +++ b/ts/sql/Interface.ts @@ -649,7 +649,9 @@ export type DataInterface = { cleanupCallHistoryMessages: () => Promise; getCallHistoryUnreadCount(): Promise; markCallHistoryRead(callId: string): Promise; - markAllCallHistoryRead(): Promise>; + markAllCallHistoryRead( + beforeTimestamp: number + ): Promise>; getCallHistoryMessageByCallId(options: { conversationId: string; callId: string; diff --git a/ts/sql/Server.ts b/ts/sql/Server.ts index 8d8f894f7f8..526cbf9ef35 100644 --- a/ts/sql/Server.ts +++ b/ts/sql/Server.ts @@ -3487,13 +3487,16 @@ async function markCallHistoryRead(callId: string): Promise { db.prepare(query).run(params); } -async function markAllCallHistoryRead(): Promise> { +async function markAllCallHistoryRead( + beforeTimestamp: number +): Promise> { const db = await getWritableInstance(); return db.transaction(() => { const where = sqlFragment` WHERE messages.type IS 'call-history' AND messages.seenStatus IS ${SEEN_STATUS_UNSEEN} + AND messages.sent_at <= ${beforeTimestamp}; `; const [selectQuery, selectParams] = sql` diff --git a/ts/state/ducks/callHistory.ts b/ts/state/ducks/callHistory.ts index 3fc3fd70aea..6fe28d18848 100644 --- a/ts/state/ducks/callHistory.ts +++ b/ts/state/ducks/callHistory.ts @@ -5,7 +5,10 @@ import type { ReadonlyDeep } from 'type-fest'; import type { ThunkAction } from 'redux-thunk'; import { omit } from 'lodash'; import type { StateType as RootStateType } from '../reducer'; -import { clearCallHistoryDataAndSync } from '../../util/callDisposition'; +import { + clearCallHistoryDataAndSync, + markAllCallHistoryReadAndSync, +} from '../../util/callDisposition'; import type { BoundActionCreatorsMapObject } from '../../hooks/useBoundActions'; import { useBoundActions } from '../../hooks/useBoundActions'; import type { ToastActionType } from './toast'; @@ -107,19 +110,8 @@ function markCallsTabViewed(): ThunkAction< CallHistoryUpdateUnread > { return async dispatch => { - try { - const conversationIds = await window.Signal.Data.markAllCallHistoryRead(); - for (const conversationId of conversationIds) { - drop(window.ConversationController.get(conversationId)?.updateUnread()); - } - } catch (error) { - log.error( - 'markCallsTabViewed: Error marking all call history read', - Errors.toLogFormat(error) - ); - } finally { - dispatch(updateCallHistoryUnreadCount()); - } + await markAllCallHistoryReadAndSync(); + dispatch(updateCallHistoryUnreadCount()); }; } diff --git a/ts/textsecure/MessageReceiver.ts b/ts/textsecure/MessageReceiver.ts index ee733d174ab..0adf507466d 100644 --- a/ts/textsecure/MessageReceiver.ts +++ b/ts/textsecure/MessageReceiver.ts @@ -3505,6 +3505,10 @@ export default class MessageReceiver callLogEvent.type === Proto.SyncMessage.CallLogEvent.Type.CLEAR ) { event = CallLogEvent.Clear; + } else if ( + callLogEvent.type === Proto.SyncMessage.CallLogEvent.Type.MARKED_AS_READ + ) { + event = CallLogEvent.MarkedAsRead; } else { throw new Error( `MessageReceiver.handleCallLogEvent: unknown type ${callLogEvent.type}` diff --git a/ts/types/CallDisposition.ts b/ts/types/CallDisposition.ts index ba7be52cd63..599393225d5 100644 --- a/ts/types/CallDisposition.ts +++ b/ts/types/CallDisposition.ts @@ -23,6 +23,7 @@ export enum CallDirection { export enum CallLogEvent { Clear = 'Clear', + MarkedAsRead = 'MarkedAsRead', } export enum LocalCallEvent { diff --git a/ts/util/callDisposition.ts b/ts/util/callDisposition.ts index ec1fcd6ff42..3c2b44fd30e 100644 --- a/ts/util/callDisposition.ts +++ b/ts/util/callDisposition.ts @@ -1056,6 +1056,49 @@ export async function clearCallHistoryDataAndSync(): Promise { } } +export async function markAllCallHistoryReadAndSync(): Promise { + try { + const timestamp = Date.now(); + + log.info( + `markAllCallHistoryReadAndSync: Marking call history read before ${timestamp}` + ); + await window.Signal.Data.markAllCallHistoryRead(timestamp); + + const ourAci = window.textsecure.storage.user.getCheckedAci(); + + const callLogEvent = new Proto.SyncMessage.CallLogEvent({ + type: Proto.SyncMessage.CallLogEvent.Type.MARKED_AS_READ, + timestamp: Long.fromNumber(timestamp), + }); + + const syncMessage = MessageSender.createSyncMessage(); + syncMessage.callLogEvent = callLogEvent; + + const contentMessage = new Proto.Content(); + contentMessage.syncMessage = syncMessage; + + const { ContentHint } = Proto.UnidentifiedSenderMessage.Message; + + log.info('markAllCallHistoryReadAndSync: Queueing sync message'); + await singleProtoJobQueue.add({ + contentHint: ContentHint.RESENDABLE, + serviceId: ourAci, + isSyncMessage: true, + protoBase64: Bytes.toBase64( + Proto.Content.encode(contentMessage).finish() + ), + type: 'callLogEventSync', + urgent: false, + }); + } catch (error) { + log.error( + 'markAllCallHistoryReadAndSync: Failed to mark call history read', + error + ); + } +} + export async function updateLocalGroupCallHistoryTimestamp( conversationId: string, callId: string, diff --git a/ts/util/onCallLogEventSync.ts b/ts/util/onCallLogEventSync.ts index a89d2a9b533..c15783fe7a9 100644 --- a/ts/util/onCallLogEventSync.ts +++ b/ts/util/onCallLogEventSync.ts @@ -25,6 +25,16 @@ export async function onCallLogEventSync( window.reduxActions.callHistory.resetCallHistory(); } confirm(); + } else if (event === CallLogEvent.MarkedAsRead) { + log.info( + `onCallLogEventSync: Marking call history read before ${timestamp}` + ); + try { + await window.Signal.Data.markAllCallHistoryRead(timestamp); + } finally { + window.reduxActions.callHistory.updateCallHistoryUnreadCount(); + } + confirm(); } else { throw missingCaseError(event); }