114 lines
3.1 KiB
TypeScript
114 lines
3.1 KiB
TypeScript
// Copyright 2023 Signal Messenger, LLC
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
import type { CallHistoryDetailsFromDiskType } from '../types/Calling';
|
|
import { CallMode } from '../types/Calling';
|
|
import type { LoggerType } from '../types/Logging';
|
|
import { strictAssert } from './assert';
|
|
import { missingCaseError } from './missingCaseError';
|
|
|
|
enum CallHistoryStatus {
|
|
Pending = 'Pending',
|
|
Missed = 'Missed',
|
|
Accepted = 'Accepted',
|
|
NotAccepted = 'NotAccepted',
|
|
}
|
|
|
|
function getCallHistoryStatus(
|
|
callHistoryDetails: CallHistoryDetailsFromDiskType
|
|
): CallHistoryStatus {
|
|
strictAssert(
|
|
callHistoryDetails.callMode === CallMode.Direct,
|
|
"Can't get call history status for group call (unimplemented)"
|
|
);
|
|
if (callHistoryDetails.acceptedTime != null) {
|
|
return CallHistoryStatus.Accepted;
|
|
}
|
|
if (callHistoryDetails.wasDeclined) {
|
|
return CallHistoryStatus.NotAccepted;
|
|
}
|
|
if (callHistoryDetails.endedTime != null) {
|
|
return CallHistoryStatus.Missed;
|
|
}
|
|
return CallHistoryStatus.Pending;
|
|
}
|
|
|
|
function isAllowedTransition(
|
|
from: CallHistoryStatus,
|
|
to: CallHistoryStatus,
|
|
log: LoggerType
|
|
): boolean {
|
|
if (from === CallHistoryStatus.Pending) {
|
|
log.info('callHistoryDetails: Can go from pending to anything.');
|
|
return true;
|
|
}
|
|
if (to === CallHistoryStatus.Pending) {
|
|
log.info("callHistoryDetails: Can't go to pending once out of it.");
|
|
return false;
|
|
}
|
|
if (from === CallHistoryStatus.Missed) {
|
|
log.info(
|
|
"callHistoryDetails: A missed call on this device might've been picked up or explicitly declined on a linked device."
|
|
);
|
|
return true;
|
|
}
|
|
if (from === CallHistoryStatus.Accepted) {
|
|
log.info(
|
|
'callHistoryDetails: If we accept anywhere that beats everything.'
|
|
);
|
|
return false;
|
|
}
|
|
if (
|
|
from === CallHistoryStatus.NotAccepted &&
|
|
to === CallHistoryStatus.Accepted
|
|
) {
|
|
log.info(
|
|
'callHistoryDetails: If we declined on this device but picked up on another device, that counts as accepted.'
|
|
);
|
|
return true;
|
|
}
|
|
|
|
if (from === CallHistoryStatus.NotAccepted) {
|
|
log.info(
|
|
"callHistoryDetails: Can't transition from NotAccepted to anything else"
|
|
);
|
|
return false;
|
|
}
|
|
|
|
throw missingCaseError(from);
|
|
}
|
|
|
|
export function validateTransition(
|
|
prev: CallHistoryDetailsFromDiskType | void,
|
|
next: CallHistoryDetailsFromDiskType,
|
|
log: LoggerType
|
|
): boolean {
|
|
// Only validating Direct calls for now
|
|
if (next.callMode !== CallMode.Direct) {
|
|
return true;
|
|
}
|
|
if (prev == null) {
|
|
return true;
|
|
}
|
|
|
|
strictAssert(
|
|
prev.callMode === CallMode.Direct && next.callMode === CallMode.Direct,
|
|
"Call mode must be 'Direct'"
|
|
);
|
|
strictAssert(prev.callId === next.callId, 'Call ID must not change');
|
|
strictAssert(
|
|
prev.wasIncoming === next.wasIncoming,
|
|
'wasIncoming must not change'
|
|
);
|
|
strictAssert(
|
|
prev.wasVideoCall === next.wasVideoCall,
|
|
'wasVideoCall must not change'
|
|
);
|
|
|
|
const before = getCallHistoryStatus(prev);
|
|
const after = getCallHistoryStatus(next);
|
|
log.info(
|
|
`callHistoryDetails: Checking transition (Call ID: ${next.callId}, Before: ${before}, After: ${after})`
|
|
);
|
|
return isAllowedTransition(before, after, log);
|
|
}
|