signal-desktop/ts/util/callingNotification.ts
2024-08-13 11:39:04 -07:00

232 lines
6.4 KiB
TypeScript

// Copyright 2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import type { LocalizerType } from '../types/Util';
import {
CallMode,
CallDirection,
DirectCallStatus,
type CallHistoryDetails,
CallType,
GroupCallStatus,
} from '../types/CallDisposition';
import { missingCaseError } from './missingCaseError';
import type { CallStatus } from '../types/CallDisposition';
import type { ConversationType } from '../state/ducks/conversations';
import { strictAssert } from './assert';
import { isMoreRecentThan } from './timestamp';
import { MINUTE } from './durations';
export type CallingNotificationType = Readonly<{
// In some older calls, we don't have a call id, this hardens against that.
callHistory: CallHistoryDetails | null;
callCreator: ConversationType | null;
activeConversationId: string | null;
groupCallEnded: boolean | null;
deviceCount: number;
maxDevices: number;
isSelectMode: boolean;
isTargeted: boolean;
}>;
export function getDirectCallNotificationText(
callDirection: CallDirection,
callType: CallType,
callStatus: DirectCallStatus,
i18n: LocalizerType
): string {
if (
callStatus === DirectCallStatus.Pending ||
callStatus === DirectCallStatus.Unknown
) {
if (callDirection === CallDirection.Incoming) {
return callType === CallType.Video
? i18n('icu:incomingVideoCall')
: i18n('icu:incomingAudioCall');
}
return callType === CallType.Video
? i18n('icu:outgoingVideoCall')
: i18n('icu:outgoingAudioCall');
}
if (callStatus === DirectCallStatus.Accepted) {
if (callDirection === CallDirection.Incoming) {
return callType === CallType.Video
? i18n('icu:acceptedIncomingVideoCall')
: i18n('icu:acceptedIncomingAudioCall');
}
return callType === CallType.Video
? i18n('icu:acceptedOutgoingVideoCall')
: i18n('icu:acceptedOutgoingAudioCall');
}
if (callStatus === DirectCallStatus.Declined) {
if (callDirection === CallDirection.Incoming) {
return callType === CallType.Video
? i18n('icu:declinedIncomingVideoCall')
: i18n('icu:declinedIncomingAudioCall');
}
return callType === CallType.Video
? i18n('icu:missedOrDeclinedOutgoingVideoCall')
: i18n('icu:missedOrDeclinedOutgoingAudioCall');
}
if (
callStatus === DirectCallStatus.Missed ||
callStatus === DirectCallStatus.MissedNotificationProfile
) {
if (callDirection === CallDirection.Incoming) {
return callType === CallType.Video
? i18n('icu:missedIncomingVideoCall')
: i18n('icu:missedIncomingAudioCall');
}
return callType === CallType.Video
? i18n('icu:missedOrDeclinedOutgoingVideoCall')
: i18n('icu:missedOrDeclinedOutgoingAudioCall');
}
if (callStatus === DirectCallStatus.Deleted) {
throw new Error(
'getDirectCallNotificationText: Cannot render deleted call'
);
}
throw missingCaseError(callStatus);
}
function getGroupCallNotificationText({
groupCallEnded,
creator,
callHistory,
i18n,
}: {
groupCallEnded: boolean;
creator: ConversationType | null;
callHistory: CallHistoryDetails;
i18n: LocalizerType;
}): string {
if (groupCallEnded) {
const { direction, status, timestamp } = callHistory;
if (direction === CallDirection.Incoming) {
if (status === GroupCallStatus.Declined) {
return i18n('icu:CallHistory__DescriptionVideoCall--Declined');
}
if (status === GroupCallStatus.Missed) {
return i18n('icu:CallHistory__DescriptionVideoCall--Missed');
}
if (isMoreRecentThan(timestamp, 5 * MINUTE)) {
return i18n('icu:calling__call-notification__ended');
}
return i18n('icu:acceptedIncomingVideoCall');
}
// Outgoing ended group calls
if (isMoreRecentThan(timestamp, 5 * MINUTE)) {
return i18n('icu:calling__call-notification__ended');
}
return i18n('icu:acceptedOutgoingVideoCall');
}
// TODO: Active call with participants DESKTOP-7439
if (creator == null) {
return i18n('icu:calling__call-notification__started-by-someone');
}
if (creator.isMe) {
return i18n('icu:calling__call-notification__started-by-you');
}
return i18n('icu:calling__call-notification__started', {
name: creator.systemGivenName ?? creator.title,
});
}
export function getCallingNotificationText(
callingNotification: CallingNotificationType,
i18n: LocalizerType
): string | null {
const {
callHistory,
callCreator: creator,
groupCallEnded,
} = callingNotification;
if (callHistory == null) {
return null;
}
if (callHistory.mode === CallMode.Direct) {
return getDirectCallNotificationText(
callHistory.direction,
callHistory.type,
callHistory.status as DirectCallStatus,
i18n
);
}
if (callHistory.mode === CallMode.Group) {
strictAssert(
groupCallEnded != null,
'getCallingNotificationText: groupCallEnded shouldnt be null for a group call'
);
return getGroupCallNotificationText({
groupCallEnded,
creator,
callHistory,
i18n,
});
}
if (callHistory.mode === CallMode.Adhoc) {
return null;
}
throw missingCaseError(callHistory.mode);
}
type CallingIconType =
| 'audio-incoming'
| 'audio-missed'
| 'audio-outgoing'
| 'phone'
| 'video'
| 'video-incoming'
| 'video-missed'
| 'video-outgoing';
export function getCallingIcon(
callType: CallType,
callDirection: CallDirection,
callStatus: CallStatus
): CallingIconType {
if (callType === CallType.Audio) {
if (callStatus === DirectCallStatus.Accepted) {
return callDirection === CallDirection.Incoming
? 'audio-incoming'
: 'audio-outgoing';
}
if (
callStatus === DirectCallStatus.Missed ||
callStatus === DirectCallStatus.Declined
) {
return 'audio-missed';
}
return 'phone';
}
if (callType === CallType.Video) {
if (callStatus === DirectCallStatus.Accepted) {
return callDirection === CallDirection.Incoming
? 'video-incoming'
: 'video-outgoing';
}
if (
callStatus === DirectCallStatus.Missed ||
callStatus === DirectCallStatus.Declined
) {
return 'video-missed';
}
return 'video';
}
if (callType === CallType.Group || callType === CallType.Adhoc) {
return 'video';
}
if (callType === CallType.Unknown) {
return 'video';
}
throw missingCaseError(callType);
}