Call Disposition
This commit is contained in:
parent
9927b132b9
commit
e5638c0b20
20 changed files with 445 additions and 53 deletions
|
@ -988,7 +988,7 @@
|
||||||
"description": "Shown in iOS theme when you or someone quotes to a message which is not from you"
|
"description": "Shown in iOS theme when you or someone quotes to a message which is not from you"
|
||||||
},
|
},
|
||||||
"audioPermissionNeeded": {
|
"audioPermissionNeeded": {
|
||||||
"message": "To send audio messages, allow Signal Desktop to access your microphone.",
|
"message": "To send voice messages, allow Signal Desktop to access your microphone.",
|
||||||
"description": "Shown if the user attempts to send an audio message without audio permissions turned on"
|
"description": "Shown if the user attempts to send an audio message without audio permissions turned on"
|
||||||
},
|
},
|
||||||
"audioCallingPermissionNeeded": {
|
"audioCallingPermissionNeeded": {
|
||||||
|
@ -1559,7 +1559,7 @@
|
||||||
"description": "when you block someone and cannot view their video"
|
"description": "when you block someone and cannot view their video"
|
||||||
},
|
},
|
||||||
"calling__block-info": {
|
"calling__block-info": {
|
||||||
"message": "You won't receive their audio or video and they won't receive yours.",
|
"message": "You won't receive their voice or video and they won't receive yours.",
|
||||||
"description": "Shown in the modal dialog to describe how blocking works in a group call"
|
"description": "Shown in the modal dialog to describe how blocking works in a group call"
|
||||||
},
|
},
|
||||||
"calling__overflow__scroll-up": {
|
"calling__overflow__scroll-up": {
|
||||||
|
@ -2765,15 +2765,23 @@
|
||||||
"description": "Shown in the shortcuts guide"
|
"description": "Shown in the shortcuts guide"
|
||||||
},
|
},
|
||||||
"Keyboard--accept-video-call": {
|
"Keyboard--accept-video-call": {
|
||||||
"message": "Accept call with video",
|
"message": "Answer call with video",
|
||||||
"description": "Shown in the calling keyboard shortcuts guide"
|
"description": "(deleted 2023/01/09) Shown in the calling keyboard shortcuts guide"
|
||||||
},
|
},
|
||||||
"Keyboard--accept-audio-call": {
|
"Keyboard--accept-audio-call": {
|
||||||
"message": "Accept call with audio",
|
"message": "Answer call with audio",
|
||||||
|
"description": "(deleted 2023/01/09) Shown in the calling keyboard shortcuts guide"
|
||||||
|
},
|
||||||
|
"icu:Keyboard--accept-video-call": {
|
||||||
|
"messageformat": "Answer call with video (video calls only)",
|
||||||
|
"description": "Shown in the calling keyboard shortcuts guide"
|
||||||
|
},
|
||||||
|
"icu:Keyboard--accept-call-without-video": {
|
||||||
|
"messageformat": "Answer call without video",
|
||||||
"description": "Shown in the calling keyboard shortcuts guide"
|
"description": "Shown in the calling keyboard shortcuts guide"
|
||||||
},
|
},
|
||||||
"Keyboard--start-audio-call": {
|
"Keyboard--start-audio-call": {
|
||||||
"message": "Start audio call",
|
"message": "Start voice call",
|
||||||
"description": "Shown in the calling keyboard shortcuts guide"
|
"description": "Shown in the calling keyboard shortcuts guide"
|
||||||
},
|
},
|
||||||
"Keyboard--start-video-call": {
|
"Keyboard--start-video-call": {
|
||||||
|
@ -3189,11 +3197,11 @@
|
||||||
"description": "When a user has no common groups, show this warning"
|
"description": "When a user has no common groups, show this warning"
|
||||||
},
|
},
|
||||||
"acceptCall": {
|
"acceptCall": {
|
||||||
"message": "Answer",
|
"message": "Answer call",
|
||||||
"description": "Shown in tooltip for the button to accept a call (audio or video)"
|
"description": "Shown in tooltip for the button to accept a call (audio or video)"
|
||||||
},
|
},
|
||||||
"acceptCallWithoutVideo": {
|
"acceptCallWithoutVideo": {
|
||||||
"message": "Answer without video",
|
"message": "Answer call without video",
|
||||||
"description": "Shown in tooltip for the button to accept a video call without video"
|
"description": "Shown in tooltip for the button to accept a video call without video"
|
||||||
},
|
},
|
||||||
"declineCall": {
|
"declineCall": {
|
||||||
|
@ -3201,40 +3209,40 @@
|
||||||
"description": "Shown in tooltip for the button to decline a call (audio or video)"
|
"description": "Shown in tooltip for the button to decline a call (audio or video)"
|
||||||
},
|
},
|
||||||
"declinedIncomingAudioCall": {
|
"declinedIncomingAudioCall": {
|
||||||
"message": "You declined an audio call",
|
"message": "You declined a voice call",
|
||||||
"description": "Shown in conversation history when you declined an incoming audio call"
|
"description": "Shown in conversation history when you declined an incoming voice call"
|
||||||
},
|
},
|
||||||
"declinedIncomingVideoCall": {
|
"declinedIncomingVideoCall": {
|
||||||
"message": "You declined a video call",
|
"message": "You declined a video call",
|
||||||
"description": "Shown in conversation history when you declined an incoming video call"
|
"description": "Shown in conversation history when you declined an incoming video call"
|
||||||
},
|
},
|
||||||
"acceptedIncomingAudioCall": {
|
"acceptedIncomingAudioCall": {
|
||||||
"message": "Incoming audio call",
|
"message": "Incoming voice call",
|
||||||
"description": "Shown in conversation history when you accepted an incoming audio call"
|
"description": "Shown in conversation history when you accepted an incoming voice call"
|
||||||
},
|
},
|
||||||
"acceptedIncomingVideoCall": {
|
"acceptedIncomingVideoCall": {
|
||||||
"message": "Incoming video call",
|
"message": "Incoming video call",
|
||||||
"description": "Shown in conversation history when you accepted an incoming video call"
|
"description": "Shown in conversation history when you accepted an incoming video call"
|
||||||
},
|
},
|
||||||
"missedIncomingAudioCall": {
|
"missedIncomingAudioCall": {
|
||||||
"message": "Missed audio call",
|
"message": "Missed voice call",
|
||||||
"description": "Shown in conversation history when you missed an incoming audio call"
|
"description": "Shown in conversation history when you missed an incoming voice call"
|
||||||
},
|
},
|
||||||
"missedIncomingVideoCall": {
|
"missedIncomingVideoCall": {
|
||||||
"message": "Missed video call",
|
"message": "Missed video call",
|
||||||
"description": "Shown in conversation history when you missed an incoming video call"
|
"description": "Shown in conversation history when you missed an incoming video call"
|
||||||
},
|
},
|
||||||
"acceptedOutgoingAudioCall": {
|
"acceptedOutgoingAudioCall": {
|
||||||
"message": "Outgoing audio call",
|
"message": "Outgoing voice call",
|
||||||
"description": "Shown in conversation history when you made an outgoing audio call"
|
"description": "Shown in conversation history when you made an outgoing voice call"
|
||||||
},
|
},
|
||||||
"acceptedOutgoingVideoCall": {
|
"acceptedOutgoingVideoCall": {
|
||||||
"message": "Outgoing video call",
|
"message": "Outgoing video call",
|
||||||
"description": "Shown in conversation history when you made an outgoing video call"
|
"description": "Shown in conversation history when you made an outgoing video call"
|
||||||
},
|
},
|
||||||
"missedOrDeclinedOutgoingAudioCall": {
|
"missedOrDeclinedOutgoingAudioCall": {
|
||||||
"message": "Unanswered audio call",
|
"message": "Unanswered voice call",
|
||||||
"description": "Shown in conversation history when your audio call is missed or declined"
|
"description": "Shown in conversation history when your voice call is missed or declined"
|
||||||
},
|
},
|
||||||
"missedOrDeclinedOutgoingVideoCall": {
|
"missedOrDeclinedOutgoingVideoCall": {
|
||||||
"message": "Unanswered video call",
|
"message": "Unanswered video call",
|
||||||
|
@ -3249,8 +3257,8 @@
|
||||||
"description": "Shown in a notification body when Signal is minimized to tray"
|
"description": "Shown in a notification body when Signal is minimized to tray"
|
||||||
},
|
},
|
||||||
"incomingAudioCall": {
|
"incomingAudioCall": {
|
||||||
"message": "Incoming audio call...",
|
"message": "Incoming voice call...",
|
||||||
"description": "Shown in both the incoming call bar and notification for an incoming audio call"
|
"description": "Shown in both the incoming call bar and notification for an incoming voice call"
|
||||||
},
|
},
|
||||||
"incomingVideoCall": {
|
"incomingVideoCall": {
|
||||||
"message": "Incoming video call...",
|
"message": "Incoming video call...",
|
||||||
|
|
|
@ -573,6 +573,33 @@ message SyncMessage {
|
||||||
optional uint32 registrationId = 3;
|
optional uint32 registrationId = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message CallEvent {
|
||||||
|
enum Type {
|
||||||
|
UNKNOWN = 0;
|
||||||
|
AUDIO_CALL = 1;
|
||||||
|
VIDEO_CALL = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Direction {
|
||||||
|
UNKNOWN = 0;
|
||||||
|
INCOMING = 1;
|
||||||
|
OUTGOING = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Event {
|
||||||
|
UNKNOWN = 0;
|
||||||
|
ACCEPTED = 1;
|
||||||
|
NOT_ACCEPTED = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
optional bytes peerUuid = 1;
|
||||||
|
optional uint64 callId = 2;
|
||||||
|
optional uint64 timestamp = 3;
|
||||||
|
optional Type type = 4;
|
||||||
|
optional Direction direction = 5;
|
||||||
|
optional Event event = 6;
|
||||||
|
}
|
||||||
|
|
||||||
optional Sent sent = 1;
|
optional Sent sent = 1;
|
||||||
optional Contacts contacts = 2;
|
optional Contacts contacts = 2;
|
||||||
optional Groups groups = 3;
|
optional Groups groups = 3;
|
||||||
|
@ -591,6 +618,7 @@ message SyncMessage {
|
||||||
repeated Viewed viewed = 16;
|
repeated Viewed viewed = 16;
|
||||||
optional PniIdentity pniIdentity = 17;
|
optional PniIdentity pniIdentity = 17;
|
||||||
optional PniChangeNumber pniChangeNumber = 18;
|
optional PniChangeNumber pniChangeNumber = 18;
|
||||||
|
optional CallEvent callEvent = 19;
|
||||||
}
|
}
|
||||||
|
|
||||||
message AttachmentPointer {
|
message AttachmentPointer {
|
||||||
|
|
|
@ -161,6 +161,7 @@ import { clearConversationDraftAttachments } from './util/clearConversationDraft
|
||||||
import { removeLinkPreview } from './services/LinkPreview';
|
import { removeLinkPreview } from './services/LinkPreview';
|
||||||
import { PanelType } from './types/Panels';
|
import { PanelType } from './types/Panels';
|
||||||
import { getQuotedMessageSelector } from './state/selectors/composer';
|
import { getQuotedMessageSelector } from './state/selectors/composer';
|
||||||
|
import { onCallEventSync } from './util/onCallEventSync';
|
||||||
|
|
||||||
const MAX_ATTACHMENT_DOWNLOAD_AGE = 3600 * 72 * 1000;
|
const MAX_ATTACHMENT_DOWNLOAD_AGE = 3600 * 72 * 1000;
|
||||||
|
|
||||||
|
@ -411,6 +412,10 @@ export async function startApp(): Promise<void> {
|
||||||
'storyRecipientUpdate',
|
'storyRecipientUpdate',
|
||||||
queuedEventListener(onStoryRecipientUpdate, false)
|
queuedEventListener(onStoryRecipientUpdate, false)
|
||||||
);
|
);
|
||||||
|
messageReceiver.addEventListener(
|
||||||
|
'callEventSync',
|
||||||
|
queuedEventListener(onCallEventSync, false)
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
ourProfileKeyService.initialize(window.storage);
|
ourProfileKeyService.initialize(window.storage);
|
||||||
|
|
|
@ -224,8 +224,10 @@ export function IncomingCallBar(props: PropsType): JSX.Element | null {
|
||||||
}, [bounceAppIconStart, bounceAppIconStop]);
|
}, [bounceAppIconStart, bounceAppIconStop]);
|
||||||
|
|
||||||
const acceptVideoCall = useCallback(() => {
|
const acceptVideoCall = useCallback(() => {
|
||||||
acceptCall({ conversationId, asVideoCall: true });
|
if (isVideoCall) {
|
||||||
}, [acceptCall, conversationId]);
|
acceptCall({ conversationId, asVideoCall: true });
|
||||||
|
}
|
||||||
|
}, [isVideoCall, acceptCall, conversationId]);
|
||||||
|
|
||||||
const acceptAudioCall = useCallback(() => {
|
const acceptAudioCall = useCallback(() => {
|
||||||
acceptCall({ conversationId, asVideoCall: false });
|
acceptCall({ conversationId, asVideoCall: false });
|
||||||
|
|
|
@ -205,11 +205,11 @@ const CALLING_SHORTCUTS: Array<ShortcutType> = [
|
||||||
keys: [['shift', 'V']],
|
keys: [['shift', 'V']],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: 'Keyboard--accept-video-call',
|
description: 'icu:Keyboard--accept-video-call',
|
||||||
keys: [['ctrlOrAlt', 'shift', 'V']],
|
keys: [['ctrlOrAlt', 'shift', 'V']],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: 'Keyboard--accept-audio-call',
|
description: 'icu:Keyboard--accept-call-without-video',
|
||||||
keys: [['ctrlOrAlt', 'shift', 'A']],
|
keys: [['ctrlOrAlt', 'shift', 'A']],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -18,6 +18,7 @@ import {
|
||||||
import { missingCaseError } from '../../util/missingCaseError';
|
import { missingCaseError } from '../../util/missingCaseError';
|
||||||
import { Tooltip, TooltipPlacement } from '../Tooltip';
|
import { Tooltip, TooltipPlacement } from '../Tooltip';
|
||||||
import * as log from '../../logging/log';
|
import * as log from '../../logging/log';
|
||||||
|
import { assertDev } from '../../util/assert';
|
||||||
|
|
||||||
export type PropsActionsType = {
|
export type PropsActionsType = {
|
||||||
returnToActiveCall: () => void;
|
returnToActiveCall: () => void;
|
||||||
|
@ -42,11 +43,14 @@ export const CallingNotification: React.FC<PropsType> = React.memo(
|
||||||
let timestamp: number;
|
let timestamp: number;
|
||||||
let wasMissed = false;
|
let wasMissed = false;
|
||||||
switch (props.callMode) {
|
switch (props.callMode) {
|
||||||
case CallMode.Direct:
|
case CallMode.Direct: {
|
||||||
timestamp = props.acceptedTime || props.endedTime;
|
const resolvedTime = props.acceptedTime ?? props.endedTime;
|
||||||
|
assertDev(resolvedTime, 'Direct call must have accepted or ended time');
|
||||||
|
timestamp = resolvedTime;
|
||||||
wasMissed =
|
wasMissed =
|
||||||
props.wasIncoming && !props.acceptedTime && !props.wasDeclined;
|
props.wasIncoming && !props.acceptedTime && !props.wasDeclined;
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case CallMode.Group:
|
case CallMode.Group:
|
||||||
timestamp = props.startedTime;
|
timestamp = props.startedTime;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -60,7 +60,7 @@ import type {
|
||||||
} from '../types/Colors';
|
} from '../types/Colors';
|
||||||
import type { MessageModel } from './messages';
|
import type { MessageModel } from './messages';
|
||||||
import { getContact } from '../messages/helpers';
|
import { getContact } from '../messages/helpers';
|
||||||
import { strictAssert } from '../util/assert';
|
import { assertDev, strictAssert } from '../util/assert';
|
||||||
import { isConversationMuted } from '../util/isConversationMuted';
|
import { isConversationMuted } from '../util/isConversationMuted';
|
||||||
import { isConversationSMSOnly } from '../util/isConversationSMSOnly';
|
import { isConversationSMSOnly } from '../util/isConversationSMSOnly';
|
||||||
import {
|
import {
|
||||||
|
@ -3226,8 +3226,11 @@ export class ConversationModel extends window.Backbone
|
||||||
let detailsToSave: CallHistoryDetailsType;
|
let detailsToSave: CallHistoryDetailsType;
|
||||||
|
|
||||||
switch (callHistoryDetails.callMode) {
|
switch (callHistoryDetails.callMode) {
|
||||||
case CallMode.Direct:
|
case CallMode.Direct: {
|
||||||
timestamp = callHistoryDetails.endedTime;
|
const resolvedTime =
|
||||||
|
callHistoryDetails.acceptedTime ?? callHistoryDetails.endedTime;
|
||||||
|
assertDev(resolvedTime, 'Direct call must have accepted or ended time');
|
||||||
|
timestamp = resolvedTime;
|
||||||
unread =
|
unread =
|
||||||
!callHistoryDetails.wasDeclined && !callHistoryDetails.acceptedTime;
|
!callHistoryDetails.wasDeclined && !callHistoryDetails.acceptedTime;
|
||||||
detailsToSave = {
|
detailsToSave = {
|
||||||
|
@ -3235,6 +3238,7 @@ export class ConversationModel extends window.Backbone
|
||||||
callMode: CallMode.Direct,
|
callMode: CallMode.Direct,
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case CallMode.Group:
|
case CallMode.Group:
|
||||||
timestamp = callHistoryDetails.startedTime;
|
timestamp = callHistoryDetails.startedTime;
|
||||||
unread = false;
|
unread = false;
|
||||||
|
@ -3257,9 +3261,20 @@ export class ConversationModel extends window.Backbone
|
||||||
// TODO: DESKTOP-722
|
// TODO: DESKTOP-722
|
||||||
} as unknown as MessageAttributesType;
|
} as unknown as MessageAttributesType;
|
||||||
|
|
||||||
|
if (callHistoryDetails.callMode === CallMode.Direct) {
|
||||||
|
const messageId = await window.Signal.Data.getCallHistoryMessageByCallId(
|
||||||
|
this.id,
|
||||||
|
callHistoryDetails.callId
|
||||||
|
);
|
||||||
|
if (messageId != null) {
|
||||||
|
message.id = messageId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const id = await window.Signal.Data.saveMessage(message, {
|
const id = await window.Signal.Data.saveMessage(message, {
|
||||||
ourUuid: window.textsecure.storage.user.getCheckedUuid().toString(),
|
ourUuid: window.textsecure.storage.user.getCheckedUuid().toString(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const model = window.MessageController.register(
|
const model = window.MessageController.register(
|
||||||
id,
|
id,
|
||||||
new window.Whisper.Message({
|
new window.Whisper.Message({
|
||||||
|
|
|
@ -36,6 +36,7 @@ import {
|
||||||
BandwidthMode,
|
BandwidthMode,
|
||||||
} from '@signalapp/ringrtc';
|
} from '@signalapp/ringrtc';
|
||||||
import { uniqBy, noop } from 'lodash';
|
import { uniqBy, noop } from 'lodash';
|
||||||
|
import Long from 'long';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
ActionsType as CallingReduxActionsType,
|
ActionsType as CallingReduxActionsType,
|
||||||
|
@ -1549,11 +1550,15 @@ export class CallingClass {
|
||||||
|
|
||||||
await this.handleOutgoingSignaling(remoteUserId, message);
|
await this.handleOutgoingSignaling(remoteUserId, message);
|
||||||
|
|
||||||
|
const callId = callingMessage.offer.callId?.toString();
|
||||||
|
assertDev(callId != null, 'Call ID missing from offer');
|
||||||
|
|
||||||
const ProtoOfferType = Proto.CallingMessage.Offer.Type;
|
const ProtoOfferType = Proto.CallingMessage.Offer.Type;
|
||||||
this.addCallHistoryForFailedIncomingCall(
|
await this.addCallHistoryForFailedIncomingCall(
|
||||||
conversation,
|
conversation,
|
||||||
callingMessage.offer.type === ProtoOfferType.OFFER_VIDEO_CALL,
|
callingMessage.offer.type === ProtoOfferType.OFFER_VIDEO_CALL,
|
||||||
envelope.timestamp
|
envelope.timestamp,
|
||||||
|
callId
|
||||||
);
|
);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
@ -1847,6 +1852,7 @@ export class CallingClass {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const callId = Long.fromValue(call.callId).toString();
|
||||||
try {
|
try {
|
||||||
// The peer must be 'trusted' before accepting a call from them.
|
// The peer must be 'trusted' before accepting a call from them.
|
||||||
// This is mostly the safety number check, unverified meaning that they were
|
// This is mostly the safety number check, unverified meaning that they were
|
||||||
|
@ -1859,10 +1865,11 @@ export class CallingClass {
|
||||||
log.info(
|
log.info(
|
||||||
`Peer is not trusted, ignoring incoming call for conversation: ${conversation.idForLogging()}`
|
`Peer is not trusted, ignoring incoming call for conversation: ${conversation.idForLogging()}`
|
||||||
);
|
);
|
||||||
this.addCallHistoryForFailedIncomingCall(
|
await this.addCallHistoryForFailedIncomingCall(
|
||||||
conversation,
|
conversation,
|
||||||
call.isVideoCall,
|
call.isVideoCall,
|
||||||
Date.now()
|
Date.now(),
|
||||||
|
callId
|
||||||
);
|
);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -1879,17 +1886,18 @@ export class CallingClass {
|
||||||
return await this.getCallSettings(conversation);
|
return await this.getCallSettings(conversation);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
log.error(`Ignoring incoming call: ${Errors.toLogFormat(err)}`);
|
log.error(`Ignoring incoming call: ${Errors.toLogFormat(err)}`);
|
||||||
this.addCallHistoryForFailedIncomingCall(
|
await this.addCallHistoryForFailedIncomingCall(
|
||||||
conversation,
|
conversation,
|
||||||
call.isVideoCall,
|
call.isVideoCall,
|
||||||
Date.now()
|
Date.now(),
|
||||||
|
callId
|
||||||
);
|
);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleAutoEndedIncomingCallRequest(
|
private async handleAutoEndedIncomingCallRequest(
|
||||||
_callId: CallId,
|
callId: CallId,
|
||||||
remoteUserId: UserId,
|
remoteUserId: UserId,
|
||||||
reason: CallEndedReason,
|
reason: CallEndedReason,
|
||||||
ageInSeconds: number,
|
ageInSeconds: number,
|
||||||
|
@ -1909,12 +1917,13 @@ export class CallingClass {
|
||||||
: 0;
|
: 0;
|
||||||
const endedTime = Date.now() - ageInMilliseconds;
|
const endedTime = Date.now() - ageInMilliseconds;
|
||||||
|
|
||||||
this.addCallHistoryForAutoEndedIncomingCall(
|
await this.addCallHistoryForAutoEndedIncomingCall(
|
||||||
conversation,
|
conversation,
|
||||||
reason,
|
reason,
|
||||||
endedTime,
|
endedTime,
|
||||||
wasVideoCall,
|
wasVideoCall,
|
||||||
receivedAtCounter
|
receivedAtCounter,
|
||||||
|
Long.fromValue(callId).toString()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1929,16 +1938,18 @@ export class CallingClass {
|
||||||
let acceptedTime: number | undefined;
|
let acceptedTime: number | undefined;
|
||||||
|
|
||||||
// eslint-disable-next-line no-param-reassign
|
// eslint-disable-next-line no-param-reassign
|
||||||
call.handleStateChanged = () => {
|
call.handleStateChanged = async () => {
|
||||||
if (call.state === CallState.Accepted) {
|
if (call.state === CallState.Accepted) {
|
||||||
acceptedTime = acceptedTime || Date.now();
|
acceptedTime = acceptedTime || Date.now();
|
||||||
} else if (call.state === CallState.Ended) {
|
} else if (call.state === CallState.Ended) {
|
||||||
this.addCallHistoryForEndedCall(conversation, call, acceptedTime);
|
await this.addCallHistoryForEndedCall(conversation, call, acceptedTime);
|
||||||
this.stopDeviceReselectionTimer();
|
this.stopDeviceReselectionTimer();
|
||||||
this.lastMediaDeviceSettings = undefined;
|
this.lastMediaDeviceSettings = undefined;
|
||||||
delete this.callsByConversation[conversation.id];
|
delete this.callsByConversation[conversation.id];
|
||||||
}
|
}
|
||||||
reduxInterface.callStateChange({
|
reduxInterface.callStateChange({
|
||||||
|
remoteUserId: call.remoteUserId,
|
||||||
|
callId: Long.fromValue(call.callId).toString(),
|
||||||
conversationId: conversation.id,
|
conversationId: conversation.id,
|
||||||
acceptedTime,
|
acceptedTime,
|
||||||
callState: call.state,
|
callState: call.state,
|
||||||
|
@ -2088,7 +2099,7 @@ export class CallingClass {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private addCallHistoryForEndedCall(
|
private async addCallHistoryForEndedCall(
|
||||||
conversation: ConversationModel,
|
conversation: ConversationModel,
|
||||||
call: Call,
|
call: Call,
|
||||||
acceptedTimeParam: number | undefined
|
acceptedTimeParam: number | undefined
|
||||||
|
@ -2110,8 +2121,11 @@ export class CallingClass {
|
||||||
acceptedTime = Date.now();
|
acceptedTime = Date.now();
|
||||||
}
|
}
|
||||||
|
|
||||||
void conversation.addCallHistory(
|
const callId = Long.fromValue(call.callId).toString();
|
||||||
|
|
||||||
|
await conversation.addCallHistory(
|
||||||
{
|
{
|
||||||
|
callId,
|
||||||
callMode: CallMode.Direct,
|
callMode: CallMode.Direct,
|
||||||
wasIncoming: call.isIncoming,
|
wasIncoming: call.isIncoming,
|
||||||
wasVideoCall: call.isVideoCall,
|
wasVideoCall: call.isVideoCall,
|
||||||
|
@ -2123,12 +2137,13 @@ export class CallingClass {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private addCallHistoryForFailedIncomingCall(
|
private async addCallHistoryForFailedIncomingCall(
|
||||||
conversation: ConversationModel,
|
conversation: ConversationModel,
|
||||||
wasVideoCall: boolean,
|
wasVideoCall: boolean,
|
||||||
timestamp: number
|
timestamp: number,
|
||||||
|
callId: string
|
||||||
) {
|
) {
|
||||||
void conversation.addCallHistory(
|
await conversation.addCallHistory(
|
||||||
{
|
{
|
||||||
callMode: CallMode.Direct,
|
callMode: CallMode.Direct,
|
||||||
wasIncoming: true,
|
wasIncoming: true,
|
||||||
|
@ -2137,17 +2152,19 @@ export class CallingClass {
|
||||||
wasDeclined: false,
|
wasDeclined: false,
|
||||||
acceptedTime: undefined,
|
acceptedTime: undefined,
|
||||||
endedTime: timestamp,
|
endedTime: timestamp,
|
||||||
|
callId,
|
||||||
},
|
},
|
||||||
undefined
|
undefined
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private addCallHistoryForAutoEndedIncomingCall(
|
private async addCallHistoryForAutoEndedIncomingCall(
|
||||||
conversation: ConversationModel,
|
conversation: ConversationModel,
|
||||||
reason: CallEndedReason,
|
reason: CallEndedReason,
|
||||||
endedTime: number,
|
endedTime: number,
|
||||||
wasVideoCall: boolean,
|
wasVideoCall: boolean,
|
||||||
receivedAtCounter: number | undefined
|
receivedAtCounter: number | undefined,
|
||||||
|
callId: string
|
||||||
) {
|
) {
|
||||||
let wasDeclined = false;
|
let wasDeclined = false;
|
||||||
let acceptedTime;
|
let acceptedTime;
|
||||||
|
@ -2159,8 +2176,9 @@ export class CallingClass {
|
||||||
}
|
}
|
||||||
// Otherwise it will show up as a missed call.
|
// Otherwise it will show up as a missed call.
|
||||||
|
|
||||||
void conversation.addCallHistory(
|
await conversation.addCallHistory(
|
||||||
{
|
{
|
||||||
|
callId,
|
||||||
callMode: CallMode.Direct,
|
callMode: CallMode.Direct,
|
||||||
wasIncoming: true,
|
wasIncoming: true,
|
||||||
wasVideoCall,
|
wasVideoCall,
|
||||||
|
|
|
@ -546,6 +546,10 @@ export type DataInterface = {
|
||||||
getLastConversationMessage(options: {
|
getLastConversationMessage(options: {
|
||||||
conversationId: string;
|
conversationId: string;
|
||||||
}): Promise<MessageType | undefined>;
|
}): Promise<MessageType | undefined>;
|
||||||
|
getCallHistoryMessageByCallId(
|
||||||
|
conversationId: string,
|
||||||
|
callId: string
|
||||||
|
): Promise<string | void>;
|
||||||
hasGroupCallHistoryMessage: (
|
hasGroupCallHistoryMessage: (
|
||||||
conversationId: string,
|
conversationId: string,
|
||||||
eraId: string
|
eraId: string
|
||||||
|
|
|
@ -256,6 +256,7 @@ const dataInterface: ServerInterface = {
|
||||||
getConversationRangeCenteredOnMessage,
|
getConversationRangeCenteredOnMessage,
|
||||||
getConversationMessageStats,
|
getConversationMessageStats,
|
||||||
getLastConversationMessage,
|
getLastConversationMessage,
|
||||||
|
getCallHistoryMessageByCallId,
|
||||||
hasGroupCallHistoryMessage,
|
hasGroupCallHistoryMessage,
|
||||||
migrateConversationMessages,
|
migrateConversationMessages,
|
||||||
|
|
||||||
|
@ -3009,6 +3010,32 @@ async function getConversationRangeCenteredOnMessage({
|
||||||
})();
|
})();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function getCallHistoryMessageByCallId(
|
||||||
|
conversationId: string,
|
||||||
|
callId: string
|
||||||
|
): Promise<string | void> {
|
||||||
|
const db = getInstance();
|
||||||
|
|
||||||
|
const id: string | void = db
|
||||||
|
.prepare<Query>(
|
||||||
|
`
|
||||||
|
SELECT id
|
||||||
|
FROM messages
|
||||||
|
WHERE conversationId = $conversationId
|
||||||
|
AND type = 'call-history'
|
||||||
|
AND callMode = 'Direct'
|
||||||
|
AND callId = $callId
|
||||||
|
`
|
||||||
|
)
|
||||||
|
.pluck()
|
||||||
|
.get({
|
||||||
|
conversationId,
|
||||||
|
callId,
|
||||||
|
});
|
||||||
|
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
async function hasGroupCallHistoryMessage(
|
async function hasGroupCallHistoryMessage(
|
||||||
conversationId: string,
|
conversationId: string,
|
||||||
eraId: string
|
eraId: string
|
||||||
|
|
38
ts/sql/migrations/72-optimize-call-id-message-lookup.ts
Normal file
38
ts/sql/migrations/72-optimize-call-id-message-lookup.ts
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
// Copyright 2021 Signal Messenger, LLC
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
import type { Database } from '@signalapp/better-sqlite3';
|
||||||
|
import type { LoggerType } from '../../types/Logging';
|
||||||
|
|
||||||
|
export default function updateToSchemaVersion72(
|
||||||
|
currentVersion: number,
|
||||||
|
db: Database,
|
||||||
|
logger: LoggerType
|
||||||
|
): void {
|
||||||
|
if (currentVersion >= 72) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
db.transaction(() => {
|
||||||
|
db.exec(
|
||||||
|
`
|
||||||
|
ALTER TABLE messages
|
||||||
|
ADD COLUMN callId TEXT
|
||||||
|
GENERATED ALWAYS AS (
|
||||||
|
json_extract(json, '$.callHistoryDetails.callId')
|
||||||
|
);
|
||||||
|
ALTER TABLE messages
|
||||||
|
ADD COLUMN callMode TEXT
|
||||||
|
GENERATED ALWAYS AS (
|
||||||
|
json_extract(json, '$.callHistoryDetails.callMode')
|
||||||
|
);
|
||||||
|
CREATE INDEX messages_call ON messages
|
||||||
|
(conversationId, type, callMode, callId);
|
||||||
|
`
|
||||||
|
);
|
||||||
|
|
||||||
|
db.pragma('user_version = 72');
|
||||||
|
})();
|
||||||
|
|
||||||
|
logger.info('updateToSchemaVersion72: success!');
|
||||||
|
}
|
|
@ -47,6 +47,7 @@ import updateToSchemaVersion68 from './68-drop-deprecated-columns';
|
||||||
import updateToSchemaVersion69 from './69-group-call-ring-cancellations';
|
import updateToSchemaVersion69 from './69-group-call-ring-cancellations';
|
||||||
import updateToSchemaVersion70 from './70-story-reply-index';
|
import updateToSchemaVersion70 from './70-story-reply-index';
|
||||||
import updateToSchemaVersion71 from './71-merge-notifications';
|
import updateToSchemaVersion71 from './71-merge-notifications';
|
||||||
|
import updateToSchemaVersion72 from './72-optimize-call-id-message-lookup';
|
||||||
|
|
||||||
function updateToSchemaVersion1(
|
function updateToSchemaVersion1(
|
||||||
currentVersion: number,
|
currentVersion: number,
|
||||||
|
@ -1963,6 +1964,7 @@ export const SCHEMA_VERSIONS = [
|
||||||
|
|
||||||
updateToSchemaVersion70,
|
updateToSchemaVersion70,
|
||||||
updateToSchemaVersion71,
|
updateToSchemaVersion71,
|
||||||
|
updateToSchemaVersion72,
|
||||||
];
|
];
|
||||||
|
|
||||||
export function updateSchema(db: Database, logger: LoggerType): void {
|
export function updateSchema(db: Database, logger: LoggerType): void {
|
||||||
|
|
|
@ -52,6 +52,8 @@ import { isDirectConversation } from '../../util/whatTypeOfConversation';
|
||||||
import { SHOW_TOAST } from './toast';
|
import { SHOW_TOAST } from './toast';
|
||||||
import { ToastType } from '../../types/Toast';
|
import { ToastType } from '../../types/Toast';
|
||||||
import type { ShowToastActionType } from './toast';
|
import type { ShowToastActionType } from './toast';
|
||||||
|
import { singleProtoJobQueue } from '../../jobs/singleProtoJobQueue';
|
||||||
|
import MessageSender from '../../textsecure/SendMessage';
|
||||||
|
|
||||||
// State
|
// State
|
||||||
|
|
||||||
|
@ -137,6 +139,8 @@ export type AcceptCallType = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export type CallStateChangeType = {
|
export type CallStateChangeType = {
|
||||||
|
remoteUserId: string; // TODO: Remove
|
||||||
|
callId: string; // TODO: Remove
|
||||||
conversationId: string;
|
conversationId: string;
|
||||||
acceptedTime?: number;
|
acceptedTime?: number;
|
||||||
callState: CallState;
|
callState: CallState;
|
||||||
|
@ -688,12 +692,67 @@ function callStateChange(
|
||||||
CallStateChangeFulfilledActionType
|
CallStateChangeFulfilledActionType
|
||||||
> {
|
> {
|
||||||
return async dispatch => {
|
return async dispatch => {
|
||||||
const { callState } = payload;
|
const {
|
||||||
|
callId,
|
||||||
|
callState,
|
||||||
|
isVideoCall,
|
||||||
|
isIncoming,
|
||||||
|
acceptedTime,
|
||||||
|
callEndedReason,
|
||||||
|
remoteUserId,
|
||||||
|
} = payload;
|
||||||
|
|
||||||
if (callState === CallState.Ended) {
|
if (callState === CallState.Ended) {
|
||||||
await callingTones.playEndCall();
|
await callingTones.playEndCall();
|
||||||
ipcRenderer.send('close-screen-share-controller');
|
ipcRenderer.send('close-screen-share-controller');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isOutgoing = !isIncoming;
|
||||||
|
const wasAccepted = acceptedTime != null;
|
||||||
|
const isConnected = callState === CallState.Accepted; // "connected"
|
||||||
|
const isEnded = callState === CallState.Ended && callEndedReason != null;
|
||||||
|
|
||||||
|
const isLocalHangup = callEndedReason === CallEndedReason.LocalHangup;
|
||||||
|
const isRemoteHangup = callEndedReason === CallEndedReason.RemoteHangup;
|
||||||
|
|
||||||
|
const answered = isConnected && wasAccepted;
|
||||||
|
const notAnswered = isEnded && !wasAccepted;
|
||||||
|
|
||||||
|
const isOutgoingRemoteAccept = isOutgoing && isConnected && answered;
|
||||||
|
const isIncomingLocalAccept = isIncoming && isConnected && answered;
|
||||||
|
const isOutgoingLocalHangup = isOutgoing && isLocalHangup && notAnswered;
|
||||||
|
const isIncomingLocalHangup = isIncoming && isLocalHangup && notAnswered;
|
||||||
|
const isOutgoingRemoteHangup = isOutgoing && isRemoteHangup && notAnswered;
|
||||||
|
const isIncomingRemoteHangup = isIncoming && isRemoteHangup && notAnswered;
|
||||||
|
|
||||||
|
if (isIncomingRemoteHangup) {
|
||||||
|
// This is considered just another "missed" event
|
||||||
|
log.info('callStateChange: not syncing hangup from self');
|
||||||
|
} else if (
|
||||||
|
isOutgoingRemoteAccept ||
|
||||||
|
isIncomingLocalAccept ||
|
||||||
|
isOutgoingLocalHangup ||
|
||||||
|
isIncomingLocalHangup ||
|
||||||
|
isOutgoingRemoteHangup
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
await singleProtoJobQueue.add(
|
||||||
|
MessageSender.getCallEventSync(
|
||||||
|
remoteUserId,
|
||||||
|
callId,
|
||||||
|
isVideoCall,
|
||||||
|
isIncoming,
|
||||||
|
acceptedTime != null
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
log.error(
|
||||||
|
'callStateChange: Failed to queue sync message',
|
||||||
|
Errors.toLogFormat(error)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: CALL_STATE_CHANGE_FULFILLED,
|
type: CALL_STATE_CHANGE_FULFILLED,
|
||||||
payload,
|
payload,
|
||||||
|
|
|
@ -37,7 +37,7 @@ import {
|
||||||
SignedPreKeys,
|
SignedPreKeys,
|
||||||
} from '../LibSignalStores';
|
} from '../LibSignalStores';
|
||||||
import { verifySignature } from '../Curve';
|
import { verifySignature } from '../Curve';
|
||||||
import { strictAssert } from '../util/assert';
|
import { assertDev, strictAssert } from '../util/assert';
|
||||||
import type { BatcherType } from '../util/batcher';
|
import type { BatcherType } from '../util/batcher';
|
||||||
import { createBatcher } from '../util/batcher';
|
import { createBatcher } from '../util/batcher';
|
||||||
import { drop } from '../util/drop';
|
import { drop } from '../util/drop';
|
||||||
|
@ -111,6 +111,7 @@ import {
|
||||||
GroupEvent,
|
GroupEvent,
|
||||||
GroupSyncEvent,
|
GroupSyncEvent,
|
||||||
StoryRecipientUpdateEvent,
|
StoryRecipientUpdateEvent,
|
||||||
|
CallEventSyncEvent,
|
||||||
} from './messageReceiverEvents';
|
} from './messageReceiverEvents';
|
||||||
import * as log from '../logging/log';
|
import * as log from '../logging/log';
|
||||||
import * as durations from '../util/durations';
|
import * as durations from '../util/durations';
|
||||||
|
@ -617,6 +618,11 @@ export default class MessageReceiver
|
||||||
handler: (ev: StoryRecipientUpdateEvent) => void
|
handler: (ev: StoryRecipientUpdateEvent) => void
|
||||||
): void;
|
): void;
|
||||||
|
|
||||||
|
public override addEventListener(
|
||||||
|
name: 'callEventSync',
|
||||||
|
handler: (ev: CallEventSyncEvent) => void
|
||||||
|
): void;
|
||||||
|
|
||||||
public override addEventListener(name: string, handler: EventHandler): void {
|
public override addEventListener(name: string, handler: EventHandler): void {
|
||||||
return super.addEventListener(name, handler);
|
return super.addEventListener(name, handler);
|
||||||
}
|
}
|
||||||
|
@ -2958,6 +2964,9 @@ export default class MessageReceiver
|
||||||
if (syncMessage.viewed && syncMessage.viewed.length) {
|
if (syncMessage.viewed && syncMessage.viewed.length) {
|
||||||
return this.handleViewed(envelope, syncMessage.viewed);
|
return this.handleViewed(envelope, syncMessage.viewed);
|
||||||
}
|
}
|
||||||
|
if (syncMessage.callEvent) {
|
||||||
|
return this.handleCallEvent(envelope, syncMessage.callEvent);
|
||||||
|
}
|
||||||
|
|
||||||
this.removeFromCache(envelope);
|
this.removeFromCache(envelope);
|
||||||
log.warn(
|
log.warn(
|
||||||
|
@ -3219,6 +3228,64 @@ export default class MessageReceiver
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async handleCallEvent(
|
||||||
|
envelope: ProcessedEnvelope,
|
||||||
|
callEvent: Proto.SyncMessage.ICallEvent
|
||||||
|
): Promise<void> {
|
||||||
|
const logId = getEnvelopeId(envelope);
|
||||||
|
log.info('MessageReceiver.handleCallEvent', logId);
|
||||||
|
const { peerUuid, callId } = callEvent;
|
||||||
|
|
||||||
|
if (!peerUuid) {
|
||||||
|
throw new Error('MessageReceiver.handleCallEvent: missing peerUuid');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!callId) {
|
||||||
|
throw new Error('MessageReceiver.handleCallEvent: missing callId');
|
||||||
|
}
|
||||||
|
|
||||||
|
logUnexpectedUrgentValue(envelope, 'callEventSync');
|
||||||
|
|
||||||
|
const peerUuidStr = bytesToUuid(peerUuid);
|
||||||
|
|
||||||
|
assertDev(
|
||||||
|
peerUuidStr != null,
|
||||||
|
'MessageReceiver.handleCallEvent: invalid peerUuid'
|
||||||
|
);
|
||||||
|
|
||||||
|
const { receivedAtCounter, timestamp } = envelope;
|
||||||
|
|
||||||
|
const wasIncoming =
|
||||||
|
callEvent.direction === Proto.SyncMessage.CallEvent.Direction.INCOMING;
|
||||||
|
const wasVideoCall =
|
||||||
|
callEvent.type === Proto.SyncMessage.CallEvent.Type.VIDEO_CALL;
|
||||||
|
const wasAccepted =
|
||||||
|
callEvent.event === Proto.SyncMessage.CallEvent.Event.ACCEPTED;
|
||||||
|
const wasDeclined =
|
||||||
|
callEvent.event === Proto.SyncMessage.CallEvent.Event.NOT_ACCEPTED;
|
||||||
|
|
||||||
|
const acceptedTime = wasAccepted ? timestamp : undefined;
|
||||||
|
const endedTime = wasDeclined ? timestamp : undefined;
|
||||||
|
|
||||||
|
const callEventSync = new CallEventSyncEvent(
|
||||||
|
{
|
||||||
|
timestamp: envelope.timestamp,
|
||||||
|
peerUuid: peerUuidStr,
|
||||||
|
callId: callId.toString(),
|
||||||
|
wasIncoming,
|
||||||
|
wasVideoCall,
|
||||||
|
wasDeclined,
|
||||||
|
acceptedTime,
|
||||||
|
endedTime,
|
||||||
|
receivedAtCounter,
|
||||||
|
},
|
||||||
|
this.removeFromCache.bind(this, envelope)
|
||||||
|
);
|
||||||
|
await this.dispatchAndWait(logId, callEventSync);
|
||||||
|
|
||||||
|
log.info('handleCallEvent: finished');
|
||||||
|
}
|
||||||
|
|
||||||
private async handleContacts(
|
private async handleContacts(
|
||||||
envelope: ProcessedEnvelope,
|
envelope: ProcessedEnvelope,
|
||||||
contacts: Proto.SyncMessage.IContacts
|
contacts: Proto.SyncMessage.IContacts
|
||||||
|
|
|
@ -1897,6 +1897,52 @@ export default class MessageSender {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static getCallEventSync(
|
||||||
|
peerUuid: string,
|
||||||
|
callId: string,
|
||||||
|
isVideoCall: boolean,
|
||||||
|
isIncoming: boolean,
|
||||||
|
isAccepted: boolean
|
||||||
|
): SingleProtoJobData {
|
||||||
|
const myUuid = window.textsecure.storage.user.getCheckedUuid();
|
||||||
|
const syncMessage = MessageSender.createSyncMessage();
|
||||||
|
|
||||||
|
const type = isVideoCall
|
||||||
|
? Proto.SyncMessage.CallEvent.Type.VIDEO_CALL
|
||||||
|
: Proto.SyncMessage.CallEvent.Type.AUDIO_CALL;
|
||||||
|
const direction = isIncoming
|
||||||
|
? Proto.SyncMessage.CallEvent.Direction.INCOMING
|
||||||
|
: Proto.SyncMessage.CallEvent.Direction.OUTGOING;
|
||||||
|
const event = isAccepted
|
||||||
|
? Proto.SyncMessage.CallEvent.Event.ACCEPTED
|
||||||
|
: Proto.SyncMessage.CallEvent.Event.NOT_ACCEPTED;
|
||||||
|
|
||||||
|
syncMessage.callEvent = new Proto.SyncMessage.CallEvent({
|
||||||
|
peerUuid: uuidToBytes(peerUuid),
|
||||||
|
callId: Long.fromString(callId),
|
||||||
|
type,
|
||||||
|
direction,
|
||||||
|
event,
|
||||||
|
timestamp: Long.fromNumber(Date.now()),
|
||||||
|
});
|
||||||
|
|
||||||
|
const contentMessage = new Proto.Content();
|
||||||
|
contentMessage.syncMessage = syncMessage;
|
||||||
|
|
||||||
|
const { ContentHint } = Proto.UnidentifiedSenderMessage.Message;
|
||||||
|
|
||||||
|
return {
|
||||||
|
contentHint: ContentHint.RESENDABLE,
|
||||||
|
identifier: myUuid.toString(),
|
||||||
|
isSyncMessage: true,
|
||||||
|
protoBase64: Bytes.toBase64(
|
||||||
|
Proto.Content.encode(contentMessage).finish()
|
||||||
|
),
|
||||||
|
type: 'callEventSync',
|
||||||
|
urgent: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
static getVerificationSync(
|
static getVerificationSync(
|
||||||
destinationE164: string | undefined,
|
destinationE164: string | undefined,
|
||||||
destinationUuid: string | undefined,
|
destinationUuid: string | undefined,
|
||||||
|
|
|
@ -404,6 +404,27 @@ export class ViewSyncEvent extends ConfirmableEvent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type CallEventSyncEventData = Readonly<{
|
||||||
|
timestamp: number;
|
||||||
|
peerUuid: string;
|
||||||
|
callId: string;
|
||||||
|
wasVideoCall: boolean;
|
||||||
|
wasIncoming: boolean;
|
||||||
|
wasDeclined: boolean;
|
||||||
|
acceptedTime: number | undefined;
|
||||||
|
endedTime: number | undefined;
|
||||||
|
receivedAtCounter: number;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
export class CallEventSyncEvent extends ConfirmableEvent {
|
||||||
|
constructor(
|
||||||
|
public readonly callEvent: CallEventSyncEventData,
|
||||||
|
confirm: ConfirmCallback
|
||||||
|
) {
|
||||||
|
super('callEventSync', confirm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export type StoryRecipientUpdateData = Readonly<{
|
export type StoryRecipientUpdateData = Readonly<{
|
||||||
destinationUuid: string;
|
destinationUuid: string;
|
||||||
storyMessageRecipients: Array<Proto.SyncMessage.Sent.IStoryMessageRecipient>;
|
storyMessageRecipients: Array<Proto.SyncMessage.Sent.IStoryMessageRecipient>;
|
||||||
|
|
|
@ -167,12 +167,13 @@ export type MediaDeviceSettings = AvailableIODevicesType & {
|
||||||
};
|
};
|
||||||
|
|
||||||
type DirectCallHistoryDetailsType = {
|
type DirectCallHistoryDetailsType = {
|
||||||
|
callId: string;
|
||||||
callMode: CallMode.Direct;
|
callMode: CallMode.Direct;
|
||||||
wasIncoming: boolean;
|
wasIncoming: boolean;
|
||||||
wasVideoCall: boolean;
|
wasVideoCall: boolean;
|
||||||
wasDeclined: boolean;
|
wasDeclined: boolean;
|
||||||
acceptedTime?: number;
|
acceptedTime?: number;
|
||||||
endedTime: number;
|
endedTime?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
type GroupCallHistoryDetailsType = {
|
type GroupCallHistoryDetailsType = {
|
||||||
|
|
|
@ -13,7 +13,7 @@ type DirectCallNotificationType = {
|
||||||
wasVideoCall: boolean;
|
wasVideoCall: boolean;
|
||||||
wasDeclined: boolean;
|
wasDeclined: boolean;
|
||||||
acceptedTime?: number;
|
acceptedTime?: number;
|
||||||
endedTime: number;
|
endedTime?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
type GroupCallNotificationType = {
|
type GroupCallNotificationType = {
|
||||||
|
|
|
@ -65,6 +65,7 @@ export const sendTypesEnum = z.enum([
|
||||||
'verificationSync',
|
'verificationSync',
|
||||||
'viewOnceSync',
|
'viewOnceSync',
|
||||||
'viewSync',
|
'viewSync',
|
||||||
|
'callEventSync',
|
||||||
|
|
||||||
// No longer used, all non-urgent
|
// No longer used, all non-urgent
|
||||||
'legacyGroupChange',
|
'legacyGroupChange',
|
||||||
|
|
46
ts/util/onCallEventSync.ts
Normal file
46
ts/util/onCallEventSync.ts
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
// Copyright 2022 Signal Messenger, LLC
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
import type { CallEventSyncEvent } from '../textsecure/messageReceiverEvents';
|
||||||
|
import * as log from '../logging/log';
|
||||||
|
import { CallMode } from '../types/Calling';
|
||||||
|
|
||||||
|
export async function onCallEventSync(
|
||||||
|
syncEvent: CallEventSyncEvent
|
||||||
|
): Promise<void> {
|
||||||
|
const { callEvent, confirm } = syncEvent;
|
||||||
|
const {
|
||||||
|
peerUuid,
|
||||||
|
callId,
|
||||||
|
wasIncoming,
|
||||||
|
wasVideoCall,
|
||||||
|
wasDeclined,
|
||||||
|
acceptedTime,
|
||||||
|
endedTime,
|
||||||
|
receivedAtCounter,
|
||||||
|
} = callEvent;
|
||||||
|
|
||||||
|
const conversation = window.ConversationController.get(peerUuid);
|
||||||
|
|
||||||
|
if (!conversation) {
|
||||||
|
log.warn(`onCallEventSync: No conversation found for peerUuid ${peerUuid}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await conversation.queueJob('onCallEventSync', async () => {
|
||||||
|
await conversation.addCallHistory(
|
||||||
|
{
|
||||||
|
callId,
|
||||||
|
callMode: CallMode.Direct,
|
||||||
|
wasDeclined,
|
||||||
|
wasIncoming,
|
||||||
|
wasVideoCall,
|
||||||
|
acceptedTime: acceptedTime ?? undefined,
|
||||||
|
endedTime: endedTime ?? undefined,
|
||||||
|
},
|
||||||
|
receivedAtCounter
|
||||||
|
);
|
||||||
|
|
||||||
|
confirm();
|
||||||
|
});
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue