Send group call update messages when joining/leaving a call
This commit is contained in:
parent
81cc8a1211
commit
b30b83ed57
4 changed files with 96 additions and 3 deletions
|
@ -228,8 +228,7 @@ message DataMessage {
|
||||||
}
|
}
|
||||||
|
|
||||||
message GroupCallUpdate {
|
message GroupCallUpdate {
|
||||||
// Currently just a sentinel message indicating that a client should
|
optional string eraId = 1;
|
||||||
// fetch updated group call state.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ProtocolVersion {
|
enum ProtocolVersion {
|
||||||
|
|
|
@ -66,6 +66,16 @@ const RINGRTC_HTTP_METHOD_TO_OUR_HTTP_METHOD: Map<
|
||||||
[HttpMethod.Delete, 'DELETE'],
|
[HttpMethod.Delete, 'DELETE'],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
// We send group call update messages to tell other clients to peek, which triggers
|
||||||
|
// notifications, timeline messages, big green "Join" buttons, and so on. This enum
|
||||||
|
// represents the three possible states we can be in. This helps ensure that we don't
|
||||||
|
// send an update on disconnect if we never sent one when we joined.
|
||||||
|
enum GroupCallUpdateMessageState {
|
||||||
|
SentNothing,
|
||||||
|
SentJoin,
|
||||||
|
SentLeft,
|
||||||
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
CallState,
|
CallState,
|
||||||
CanvasVideoRenderer,
|
CanvasVideoRenderer,
|
||||||
|
@ -379,6 +389,7 @@ export class CallingClass {
|
||||||
|
|
||||||
const groupIdBuffer = base64ToArrayBuffer(groupId);
|
const groupIdBuffer = base64ToArrayBuffer(groupId);
|
||||||
|
|
||||||
|
let updateMessageState = GroupCallUpdateMessageState.SentNothing;
|
||||||
let isRequestingMembershipProof = false;
|
let isRequestingMembershipProof = false;
|
||||||
|
|
||||||
const outerGroupCall = RingRTC.getGroupCall(
|
const outerGroupCall = RingRTC.getGroupCall(
|
||||||
|
@ -387,6 +398,7 @@ export class CallingClass {
|
||||||
{
|
{
|
||||||
onLocalDeviceStateChanged: groupCall => {
|
onLocalDeviceStateChanged: groupCall => {
|
||||||
const localDeviceState = groupCall.getLocalDeviceState();
|
const localDeviceState = groupCall.getLocalDeviceState();
|
||||||
|
const { eraId } = groupCall.getPeekInfo() || {};
|
||||||
|
|
||||||
if (
|
if (
|
||||||
localDeviceState.connectionState === ConnectionState.NotConnected
|
localDeviceState.connectionState === ConnectionState.NotConnected
|
||||||
|
@ -397,6 +409,14 @@ export class CallingClass {
|
||||||
this.disableLocalCamera();
|
this.disableLocalCamera();
|
||||||
|
|
||||||
delete this.callsByConversation[conversationId];
|
delete this.callsByConversation[conversationId];
|
||||||
|
|
||||||
|
if (
|
||||||
|
updateMessageState === GroupCallUpdateMessageState.SentJoin &&
|
||||||
|
eraId
|
||||||
|
) {
|
||||||
|
updateMessageState = GroupCallUpdateMessageState.SentLeft;
|
||||||
|
this.sendGroupCallUpdateMessage(conversationId, eraId);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
this.callsByConversation[conversationId] = groupCall;
|
this.callsByConversation[conversationId] = groupCall;
|
||||||
|
|
||||||
|
@ -406,6 +426,15 @@ export class CallingClass {
|
||||||
} else {
|
} else {
|
||||||
this.videoCapturer.enableCaptureAndSend(groupCall);
|
this.videoCapturer.enableCaptureAndSend(groupCall);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
updateMessageState === GroupCallUpdateMessageState.SentNothing &&
|
||||||
|
localDeviceState.joinState === JoinState.Joined &&
|
||||||
|
eraId
|
||||||
|
) {
|
||||||
|
updateMessageState = GroupCallUpdateMessageState.SentJoin;
|
||||||
|
this.sendGroupCallUpdateMessage(conversationId, eraId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.syncGroupCallToRedux(conversationId, groupCall);
|
this.syncGroupCallToRedux(conversationId, groupCall);
|
||||||
|
@ -632,6 +661,35 @@ export class CallingClass {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private sendGroupCallUpdateMessage(
|
||||||
|
conversationId: string,
|
||||||
|
eraId: string
|
||||||
|
): void {
|
||||||
|
const conversation = window.ConversationController.get(conversationId);
|
||||||
|
if (!conversation) {
|
||||||
|
window.log.error(
|
||||||
|
'Unable to send group call update message for non-existent conversation'
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const groupV2 = conversation.getGroupV2Info();
|
||||||
|
const sendOptions = conversation.getSendOptions();
|
||||||
|
if (!groupV2) {
|
||||||
|
window.log.error(
|
||||||
|
'Unable to send group call update message for conversation that lacks groupV2 info'
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We "fire and forget" because sending this message is non-essential.
|
||||||
|
window.textsecure.messaging
|
||||||
|
.sendGroupCallUpdate({ eraId, groupV2 }, sendOptions)
|
||||||
|
.catch(err => {
|
||||||
|
window.log.error('Failed to send group call update', err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
async accept(conversationId: string, asVideoCall: boolean): Promise<void> {
|
async accept(conversationId: string, asVideoCall: boolean): Promise<void> {
|
||||||
window.log.info('CallingClass.accept()');
|
window.log.info('CallingClass.accept()');
|
||||||
|
|
||||||
|
|
4
ts/textsecure.d.ts
vendored
4
ts/textsecure.d.ts
vendored
|
@ -653,7 +653,9 @@ export declare namespace DataMessageClass {
|
||||||
data?: AttachmentPointerClass;
|
data?: AttachmentPointerClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
class GroupCallUpdate {}
|
class GroupCallUpdate {
|
||||||
|
eraId?: string;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: we need to use namespaces to express nested classes in Typescript
|
// Note: we need to use namespaces to express nested classes in Typescript
|
||||||
|
|
|
@ -105,6 +105,10 @@ type GroupV1InfoType = {
|
||||||
members: Array<string>;
|
members: Array<string>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
interface GroupCallUpdateType {
|
||||||
|
eraId: string;
|
||||||
|
}
|
||||||
|
|
||||||
type MessageOptionsType = {
|
type MessageOptionsType = {
|
||||||
attachments?: Array<AttachmentType> | null;
|
attachments?: Array<AttachmentType> | null;
|
||||||
body?: string;
|
body?: string;
|
||||||
|
@ -125,6 +129,7 @@ type MessageOptionsType = {
|
||||||
deletedForEveryoneTimestamp?: number;
|
deletedForEveryoneTimestamp?: number;
|
||||||
timestamp: number;
|
timestamp: number;
|
||||||
mentions?: BodyRangesType;
|
mentions?: BodyRangesType;
|
||||||
|
groupCallUpdate?: GroupCallUpdateType;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Message {
|
class Message {
|
||||||
|
@ -180,6 +185,8 @@ class Message {
|
||||||
|
|
||||||
mentions?: BodyRangesType;
|
mentions?: BodyRangesType;
|
||||||
|
|
||||||
|
groupCallUpdate?: GroupCallUpdateType;
|
||||||
|
|
||||||
constructor(options: MessageOptionsType) {
|
constructor(options: MessageOptionsType) {
|
||||||
this.attachments = options.attachments || [];
|
this.attachments = options.attachments || [];
|
||||||
this.body = options.body;
|
this.body = options.body;
|
||||||
|
@ -197,6 +204,7 @@ class Message {
|
||||||
this.timestamp = options.timestamp;
|
this.timestamp = options.timestamp;
|
||||||
this.deletedForEveryoneTimestamp = options.deletedForEveryoneTimestamp;
|
this.deletedForEveryoneTimestamp = options.deletedForEveryoneTimestamp;
|
||||||
this.mentions = options.mentions;
|
this.mentions = options.mentions;
|
||||||
|
this.groupCallUpdate = options.groupCallUpdate;
|
||||||
|
|
||||||
if (!(this.recipients instanceof Array)) {
|
if (!(this.recipients instanceof Array)) {
|
||||||
throw new Error('Invalid recipient list');
|
throw new Error('Invalid recipient list');
|
||||||
|
@ -386,6 +394,15 @@ class Message {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.groupCallUpdate) {
|
||||||
|
const { GroupCallUpdate } = window.textsecure.protobuf.DataMessage;
|
||||||
|
|
||||||
|
const groupCallUpdate = new GroupCallUpdate();
|
||||||
|
groupCallUpdate.eraId = this.groupCallUpdate.eraId;
|
||||||
|
|
||||||
|
proto.groupCallUpdate = groupCallUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
this.dataMessage = proto;
|
this.dataMessage = proto;
|
||||||
return proto;
|
return proto;
|
||||||
}
|
}
|
||||||
|
@ -1126,6 +1143,20 @@ export default class MessageSender {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async sendGroupCallUpdate(
|
||||||
|
{ groupV2, eraId }: { groupV2: GroupV2InfoType; eraId: string },
|
||||||
|
options?: SendOptionsType
|
||||||
|
): Promise<void> {
|
||||||
|
await this.sendMessageToGroup(
|
||||||
|
{
|
||||||
|
groupV2,
|
||||||
|
groupCallUpdate: { eraId },
|
||||||
|
timestamp: Date.now(),
|
||||||
|
},
|
||||||
|
options
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
async sendDeliveryReceipt(
|
async sendDeliveryReceipt(
|
||||||
recipientE164: string,
|
recipientE164: string,
|
||||||
recipientUuid: string,
|
recipientUuid: string,
|
||||||
|
@ -1630,6 +1661,7 @@ export default class MessageSender {
|
||||||
deletedForEveryoneTimestamp,
|
deletedForEveryoneTimestamp,
|
||||||
timestamp,
|
timestamp,
|
||||||
mentions,
|
mentions,
|
||||||
|
groupCallUpdate,
|
||||||
}: {
|
}: {
|
||||||
attachments?: Array<AttachmentType>;
|
attachments?: Array<AttachmentType>;
|
||||||
expireTimer?: number;
|
expireTimer?: number;
|
||||||
|
@ -1644,6 +1676,7 @@ export default class MessageSender {
|
||||||
deletedForEveryoneTimestamp?: number;
|
deletedForEveryoneTimestamp?: number;
|
||||||
timestamp: number;
|
timestamp: number;
|
||||||
mentions?: BodyRangesType;
|
mentions?: BodyRangesType;
|
||||||
|
groupCallUpdate?: GroupCallUpdateType;
|
||||||
},
|
},
|
||||||
options?: SendOptionsType
|
options?: SendOptionsType
|
||||||
): Promise<CallbackResultType> {
|
): Promise<CallbackResultType> {
|
||||||
|
@ -1695,6 +1728,7 @@ export default class MessageSender {
|
||||||
}
|
}
|
||||||
: undefined,
|
: undefined,
|
||||||
mentions,
|
mentions,
|
||||||
|
groupCallUpdate,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (recipients.length === 0) {
|
if (recipients.length === 0) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue