Mark all calls read when opening calls tab

This commit is contained in:
Jamie Kyle 2023-08-22 14:01:36 -07:00 committed by GitHub
parent b7c17212c7
commit 344ebf494d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 80 additions and 17 deletions

View file

@ -223,7 +223,7 @@ export class ConversationModel extends window.Backbone
contactCollection?: Backbone.Collection<ConversationModel>; contactCollection?: Backbone.Collection<ConversationModel>;
debouncedUpdateLastMessage?: (() => void) & { flush(): void }; debouncedUpdateLastMessage: (() => void) & { flush(): void };
initialPromise?: Promise<unknown>; initialPromise?: Promise<unknown>;
@ -1400,9 +1400,7 @@ export class ConversationModel extends window.Backbone
): Promise<void> { ): Promise<void> {
await this.beforeAddSingleMessage(message); await this.beforeAddSingleMessage(message);
this.doAddSingleMessage(message, { isJustSent }); this.doAddSingleMessage(message, { isJustSent });
this.debouncedUpdateLastMessage();
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
this.debouncedUpdateLastMessage!();
} }
private async beforeAddSingleMessage(message: MessageModel): Promise<void> { private async beforeAddSingleMessage(message: MessageModel): Promise<void> {
@ -5221,7 +5219,7 @@ export class ConversationModel extends window.Backbone
async flushDebouncedUpdates(): Promise<void> { async flushDebouncedUpdates(): Promise<void> {
try { try {
await this.debouncedUpdateLastMessage?.flush(); this.debouncedUpdateLastMessage.flush();
} catch (error) { } catch (error) {
const logId = this.idForLogging(); const logId = this.idForLogging();
log.error( log.error(

View file

@ -1148,7 +1148,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
sticker: undefined, sticker: undefined,
...additionalProperties, ...additionalProperties,
}); });
this.getConversation()?.debouncedUpdateLastMessage?.(); this.getConversation()?.debouncedUpdateLastMessage();
if (shouldPersist) { if (shouldPersist) {
await window.Signal.Data.saveMessage(this.attributes, { await window.Signal.Data.saveMessage(this.attributes, {
@ -1485,7 +1485,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
saveErrors?: (errors: Array<Error>) => void saveErrors?: (errors: Array<Error>) => void
): Promise<void> { ): Promise<void> {
const updateLeftPane = const updateLeftPane =
this.getConversation()?.debouncedUpdateLastMessage || noop; this.getConversation()?.debouncedUpdateLastMessage ?? noop;
updateLeftPane(); updateLeftPane();

View file

@ -637,6 +637,7 @@ export type DataInterface = {
clearCallHistory: (beforeTimestamp: number) => Promise<Array<string>>; clearCallHistory: (beforeTimestamp: number) => Promise<Array<string>>;
getCallHistoryUnreadCount(): Promise<number>; getCallHistoryUnreadCount(): Promise<number>;
markCallHistoryRead(callId: string): Promise<void>; markCallHistoryRead(callId: string): Promise<void>;
markAllCallHistoryRead(): Promise<ReadonlyArray<string>>;
getCallHistoryMessageByCallId(options: { getCallHistoryMessageByCallId(options: {
conversationId: string; conversationId: string;
callId: string; callId: string;

View file

@ -308,6 +308,7 @@ const dataInterface: ServerInterface = {
clearCallHistory, clearCallHistory,
getCallHistoryUnreadCount, getCallHistoryUnreadCount,
markCallHistoryRead, markCallHistoryRead,
markAllCallHistoryRead,
getCallHistoryMessageByCallId, getCallHistoryMessageByCallId,
getCallHistory, getCallHistory,
getCallHistoryGroupsCount, getCallHistoryGroupsCount,
@ -3369,6 +3370,35 @@ async function markCallHistoryRead(callId: string): Promise<void> {
db.prepare(query).run(params); db.prepare(query).run(params);
} }
async function markAllCallHistoryRead(): Promise<ReadonlyArray<string>> {
const db = getInstance();
return db.transaction(() => {
const where = sqlFragment`
WHERE messages.type IS 'call-history'
AND messages.readStatus IS ${READ_STATUS_UNREAD}
`;
const [selectQuery, selectParams] = sql`
SELECT DISTINCT conversationId
FROM messages
${where};
`;
const conversationIds = db.prepare(selectQuery).pluck().all(selectParams);
const [updateQuery, updateParams] = sql`
UPDATE messages
SET readStatus = ${READ_STATUS_READ}
${where};
`;
db.prepare(updateQuery).run(updateParams);
return conversationIds;
})();
}
function getCallHistoryGroupDataSync( function getCallHistoryGroupDataSync(
db: Database, db: Database,
isCount: boolean, isCount: boolean,

View file

@ -13,6 +13,7 @@ import { ToastType } from '../../types/Toast';
import type { CallHistoryDetails } from '../../types/CallDisposition'; import type { CallHistoryDetails } from '../../types/CallDisposition';
import * as log from '../../logging/log'; import * as log from '../../logging/log';
import * as Errors from '../../types/errors'; import * as Errors from '../../types/errors';
import { drop } from '../../util/drop';
export type CallHistoryState = ReadonlyDeep<{ export type CallHistoryState = ReadonlyDeep<{
// This informs the app that underlying call history data has changed. // This informs the app that underlying call history data has changed.
@ -77,9 +78,35 @@ function markCallHistoryRead(
return async dispatch => { return async dispatch => {
try { try {
await window.Signal.Data.markCallHistoryRead(callId); await window.Signal.Data.markCallHistoryRead(callId);
await window.ConversationController.get(conversationId)?.updateUnread(); drop(window.ConversationController.get(conversationId)?.updateUnread());
} catch (error) { } catch (error) {
log.error('Error marking call history read', Errors.toLogFormat(error)); log.error(
'markCallHistoryRead: Error marking call history read',
Errors.toLogFormat(error)
);
} finally {
dispatch(updateCallHistoryUnreadCount());
}
};
}
function markCallsTabViewed(): ThunkAction<
void,
RootStateType,
unknown,
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 { } finally {
dispatch(updateCallHistoryUnreadCount()); dispatch(updateCallHistoryUnreadCount());
} }
@ -118,6 +145,7 @@ export const actions = {
clearAllCallHistory, clearAllCallHistory,
updateCallHistoryUnreadCount, updateCallHistoryUnreadCount,
markCallHistoryRead, markCallHistoryRead,
markCallsTabViewed,
}; };
export const useCallHistoryActions = (): BoundActionCreatorsMapObject< export const useCallHistoryActions = (): BoundActionCreatorsMapObject<

View file

@ -1,7 +1,7 @@
// Copyright 2023 Signal Messenger, LLC // Copyright 2023 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-License-Identifier: AGPL-3.0-only
import React, { useCallback } from 'react'; import React, { useCallback, useEffect } from 'react';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import { useItemsActions } from '../ducks/items'; import { useItemsActions } from '../ducks/items';
import { import {
@ -102,8 +102,11 @@ export function SmartCallsTab(): JSX.Element {
onOutgoingAudioCallInConversation, onOutgoingAudioCallInConversation,
onOutgoingVideoCallInConversation, onOutgoingVideoCallInConversation,
} = useCallingActions(); } = useCallingActions();
const { clearAllCallHistory: clearCallHistory, markCallHistoryRead } = const {
useCallHistoryActions(); clearAllCallHistory: clearCallHistory,
markCallHistoryRead,
markCallsTabViewed,
} = useCallHistoryActions();
const getCallHistoryGroupsCount = useCallback( const getCallHistoryGroupsCount = useCallback(
async (options: CallHistoryFilterOptions) => { async (options: CallHistoryFilterOptions) => {
@ -149,6 +152,10 @@ export function SmartCallsTab(): JSX.Element {
[allConversations, regionCode, callHistoryEdition] [allConversations, regionCode, callHistoryEdition]
); );
useEffect(() => {
markCallsTabViewed();
}, [markCallsTabViewed]);
return ( return (
<CallsTab <CallsTab
activeCall={activeCall} activeCall={activeCall}

View file

@ -59,7 +59,6 @@ import {
callDetailsSchema, callDetailsSchema,
} from '../types/CallDisposition'; } from '../types/CallDisposition';
import type { ConversationType } from '../state/ducks/conversations'; import type { ConversationType } from '../state/ducks/conversations';
import { drop } from './drop';
// utils // utils
// ----- // -----
@ -895,7 +894,7 @@ export async function clearCallHistoryDataAndSync(): Promise<void> {
messageId, messageId,
message.get('conversationId') message.get('conversationId')
); );
drop(conversation.updateLastMessage()); conversation.debouncedUpdateLastMessage();
window.MessageController.unregister(messageId); window.MessageController.unregister(messageId);
}); });

View file

@ -15,7 +15,7 @@ export async function cleanupMessage(
window.reduxActions?.conversations.messageDeleted(id, conversationId); window.reduxActions?.conversations.messageDeleted(id, conversationId);
const parentConversation = window.ConversationController.get(conversationId); const parentConversation = window.ConversationController.get(conversationId);
parentConversation?.debouncedUpdateLastMessage?.(); parentConversation?.debouncedUpdateLastMessage();
window.MessageController.unregister(id); window.MessageController.unregister(id);