Fix call history deletion from sync messages

This commit is contained in:
Jamie Kyle 2023-09-27 12:42:30 -07:00 committed by GitHub
parent 20ddca9684
commit 1cc478180e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 84 additions and 11 deletions

View file

@ -8,6 +8,7 @@ import { strictAssert } from '../util/assert';
let callsHistoryData: ReadonlyArray<CallHistoryDetails>; let callsHistoryData: ReadonlyArray<CallHistoryDetails>;
export async function loadCallsHistory(): Promise<void> { export async function loadCallsHistory(): Promise<void> {
await dataInterface.cleanupCallHistoryMessages();
callsHistoryData = await dataInterface.getAllCallHistory(); callsHistoryData = await dataInterface.getAllCallHistory();
} }

View file

@ -630,6 +630,7 @@ export type DataInterface = {
}): Promise<MessageType | undefined>; }): Promise<MessageType | undefined>;
getAllCallHistory: () => Promise<ReadonlyArray<CallHistoryDetails>>; getAllCallHistory: () => Promise<ReadonlyArray<CallHistoryDetails>>;
clearCallHistory: (beforeTimestamp: number) => Promise<Array<string>>; clearCallHistory: (beforeTimestamp: number) => Promise<Array<string>>;
cleanupCallHistoryMessages: () => Promise<void>;
getCallHistoryUnreadCount(): Promise<number>; getCallHistoryUnreadCount(): Promise<number>;
markCallHistoryRead(callId: string): Promise<void>; markCallHistoryRead(callId: string): Promise<void>;
markAllCallHistoryRead(): Promise<ReadonlyArray<string>>; markAllCallHistoryRead(): Promise<ReadonlyArray<string>>;

View file

@ -305,6 +305,7 @@ const dataInterface: ServerInterface = {
getLastConversationMessage, getLastConversationMessage,
getAllCallHistory, getAllCallHistory,
clearCallHistory, clearCallHistory,
cleanupCallHistoryMessages,
getCallHistoryUnreadCount, getCallHistoryUnreadCount,
markCallHistoryRead, markCallHistoryRead,
markAllCallHistoryRead, markAllCallHistoryRead,
@ -3294,6 +3295,24 @@ async function clearCallHistory(
})(); })();
} }
async function cleanupCallHistoryMessages(): Promise<void> {
const db = getInstance();
return db
.transaction(() => {
const [query, params] = sql`
DELETE FROM messages
WHERE messages.id IN (
SELECT messages.id FROM messages
LEFT JOIN callsHistory ON callsHistory.callId IS messages.callId
WHERE messages.type IS 'call-history'
AND callsHistory.status IS ${CALL_STATUS_DELETED}
)
`;
db.prepare(query).run(params);
})
.immediate();
}
async function getCallHistoryMessageByCallId(options: { async function getCallHistoryMessageByCallId(options: {
conversationId: string; conversationId: string;
callId: string; callId: string;

View file

@ -3,6 +3,7 @@
import type { ReadonlyDeep } from 'type-fest'; import type { ReadonlyDeep } from 'type-fest';
import type { ThunkAction } from 'redux-thunk'; import type { ThunkAction } from 'redux-thunk';
import { omit } from 'lodash';
import type { StateType as RootStateType } from '../reducer'; import type { StateType as RootStateType } from '../reducer';
import { clearCallHistoryDataAndSync } from '../../util/callDisposition'; import { clearCallHistoryDataAndSync } from '../../util/callDisposition';
import type { BoundActionCreatorsMapObject } from '../../hooks/useBoundActions'; import type { BoundActionCreatorsMapObject } from '../../hooks/useBoundActions';
@ -22,15 +23,21 @@ export type CallHistoryState = ReadonlyDeep<{
callHistoryByCallId: Record<string, CallHistoryDetails>; callHistoryByCallId: Record<string, CallHistoryDetails>;
}>; }>;
const CALL_HISTORY_CACHE = 'callHistory/CACHE'; const CALL_HISTORY_ADD = 'callHistory/ADD';
const CALL_HISTORY_REMOVE = 'callHistory/REMOVE';
const CALL_HISTORY_RESET = 'callHistory/RESET'; const CALL_HISTORY_RESET = 'callHistory/RESET';
const CALL_HISTORY_UPDATE_UNREAD = 'callHistory/UPDATE_UNREAD'; const CALL_HISTORY_UPDATE_UNREAD = 'callHistory/UPDATE_UNREAD';
export type CallHistoryCache = ReadonlyDeep<{ export type CallHistoryAdd = ReadonlyDeep<{
type: typeof CALL_HISTORY_CACHE; type: typeof CALL_HISTORY_ADD;
payload: CallHistoryDetails; payload: CallHistoryDetails;
}>; }>;
export type CallHistoryRemove = ReadonlyDeep<{
type: typeof CALL_HISTORY_REMOVE;
payload: CallHistoryDetails['callId'];
}>;
export type CallHistoryReset = ReadonlyDeep<{ export type CallHistoryReset = ReadonlyDeep<{
type: typeof CALL_HISTORY_RESET; type: typeof CALL_HISTORY_RESET;
}>; }>;
@ -41,7 +48,10 @@ export type CallHistoryUpdateUnread = ReadonlyDeep<{
}>; }>;
export type CallHistoryAction = ReadonlyDeep< export type CallHistoryAction = ReadonlyDeep<
CallHistoryCache | CallHistoryReset | CallHistoryUpdateUnread | CallHistoryAdd
| CallHistoryRemove
| CallHistoryReset
| CallHistoryUpdateUnread
>; >;
export function getEmptyState(): CallHistoryState { export function getEmptyState(): CallHistoryState {
@ -113,13 +123,26 @@ function markCallsTabViewed(): ThunkAction<
}; };
} }
function cacheCallHistory(callHistory: CallHistoryDetails): CallHistoryCache { function addCallHistory(callHistory: CallHistoryDetails): CallHistoryAdd {
return { return {
type: CALL_HISTORY_CACHE, type: CALL_HISTORY_ADD,
payload: callHistory, payload: callHistory,
}; };
} }
function removeCallHistory(
callId: CallHistoryDetails['callId']
): CallHistoryRemove {
return {
type: CALL_HISTORY_REMOVE,
payload: callId,
};
}
function resetCallHistory(): CallHistoryReset {
return { type: CALL_HISTORY_RESET };
}
function clearAllCallHistory(): ThunkAction< function clearAllCallHistory(): ThunkAction<
void, void,
RootStateType, RootStateType,
@ -134,14 +157,16 @@ function clearAllCallHistory(): ThunkAction<
log.error('Error clearing call history', Errors.toLogFormat(error)); log.error('Error clearing call history', Errors.toLogFormat(error));
} finally { } finally {
// Just force a reset, even if the clear failed. // Just force a reset, even if the clear failed.
dispatch({ type: CALL_HISTORY_RESET }); dispatch(resetCallHistory());
dispatch(updateCallHistoryUnreadCount()); dispatch(updateCallHistoryUnreadCount());
} }
}; };
} }
export const actions = { export const actions = {
cacheCallHistory, addCallHistory,
removeCallHistory,
resetCallHistory,
clearAllCallHistory, clearAllCallHistory,
updateCallHistoryUnreadCount, updateCallHistoryUnreadCount,
markCallHistoryRead, markCallHistoryRead,
@ -159,7 +184,7 @@ export function reducer(
switch (action.type) { switch (action.type) {
case CALL_HISTORY_RESET: case CALL_HISTORY_RESET:
return { ...state, edition: state.edition + 1, callHistoryByCallId: {} }; return { ...state, edition: state.edition + 1, callHistoryByCallId: {} };
case CALL_HISTORY_CACHE: case CALL_HISTORY_ADD:
return { return {
...state, ...state,
callHistoryByCallId: { callHistoryByCallId: {
@ -167,6 +192,11 @@ export function reducer(
[action.payload.callId]: action.payload, [action.payload.callId]: action.payload,
}, },
}; };
case CALL_HISTORY_REMOVE:
return {
...state,
callHistoryByCallId: omit(state.callHistoryByCallId, action.payload),
};
case CALL_HISTORY_UPDATE_UNREAD: case CALL_HISTORY_UPDATE_UNREAD:
return { return {
...state, ...state,

View file

@ -785,8 +785,18 @@ async function updateLocalCallHistory(
'updateLocalCallHistory: Saving call history:', 'updateLocalCallHistory: Saving call history:',
formatCallHistory(callHistory) formatCallHistory(callHistory)
); );
const isDeleted =
callHistory.status === DirectCallStatus.Deleted ||
callHistory.status === GroupCallStatus.Deleted;
await window.Signal.Data.saveCallHistory(callHistory); await window.Signal.Data.saveCallHistory(callHistory);
window.reduxActions.callHistory.cacheCallHistory(callHistory);
if (isDeleted) {
window.reduxActions.callHistory.removeCallHistory(callHistory.callId);
} else {
window.reduxActions.callHistory.addCallHistory(callHistory);
}
const prevMessage = const prevMessage =
await window.Signal.Data.getCallHistoryMessageByCallId({ await window.Signal.Data.getCallHistoryMessageByCallId({
@ -806,6 +816,13 @@ async function updateLocalCallHistory(
); );
} }
if (isDeleted) {
if (prevMessage != null) {
await window.Signal.Data.removeMessage(prevMessage.id);
}
return callHistory;
}
let unread = false; let unread = false;
if (callHistory.mode === CallMode.Direct) { if (callHistory.mode === CallMode.Direct) {
unread = unread =

View file

@ -18,7 +18,12 @@ export async function onCallLogEventSync(
if (event === CallLogEvent.Clear) { if (event === CallLogEvent.Clear) {
log.info(`onCallLogEventSync: Clearing call history before ${timestamp}`); log.info(`onCallLogEventSync: Clearing call history before ${timestamp}`);
await window.Signal.Data.clearCallHistory(timestamp); try {
await window.Signal.Data.clearCallHistory(timestamp);
} finally {
// We want to reset the call history even if the clear fails.
window.reduxActions.callHistory.resetCallHistory();
}
confirm(); confirm();
} else { } else {
throw missingCaseError(event); throw missingCaseError(event);