Skip to content

Commit

Permalink
Send group call update messages when joining/leaving a call
Browse files Browse the repository at this point in the history
  • Loading branch information
EvanHahn-Signal committed Dec 2, 2020
1 parent 81cc8a1 commit b30b83e
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 3 deletions.
3 changes: 1 addition & 2 deletions protos/SignalService.proto
Expand Up @@ -228,8 +228,7 @@ message DataMessage {
}

message GroupCallUpdate {
// Currently just a sentinel message indicating that a client should
// fetch updated group call state.
optional string eraId = 1;
}

enum ProtocolVersion {
Expand Down
58 changes: 58 additions & 0 deletions ts/services/calling.ts
Expand Up @@ -66,6 +66,16 @@ const RINGRTC_HTTP_METHOD_TO_OUR_HTTP_METHOD: Map<
[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 {
CallState,
CanvasVideoRenderer,
Expand Down Expand Up @@ -379,6 +389,7 @@ export class CallingClass {

const groupIdBuffer = base64ToArrayBuffer(groupId);

let updateMessageState = GroupCallUpdateMessageState.SentNothing;
let isRequestingMembershipProof = false;

const outerGroupCall = RingRTC.getGroupCall(
Expand All @@ -387,6 +398,7 @@ export class CallingClass {
{
onLocalDeviceStateChanged: groupCall => {
const localDeviceState = groupCall.getLocalDeviceState();
const { eraId } = groupCall.getPeekInfo() || {};

if (
localDeviceState.connectionState === ConnectionState.NotConnected
Expand All @@ -397,6 +409,14 @@ export class CallingClass {
this.disableLocalCamera();

delete this.callsByConversation[conversationId];

if (
updateMessageState === GroupCallUpdateMessageState.SentJoin &&
eraId
) {
updateMessageState = GroupCallUpdateMessageState.SentLeft;
this.sendGroupCallUpdateMessage(conversationId, eraId);
}
} else {
this.callsByConversation[conversationId] = groupCall;

Expand All @@ -406,6 +426,15 @@ export class CallingClass {
} else {
this.videoCapturer.enableCaptureAndSend(groupCall);
}

if (
updateMessageState === GroupCallUpdateMessageState.SentNothing &&
localDeviceState.joinState === JoinState.Joined &&
eraId
) {
updateMessageState = GroupCallUpdateMessageState.SentJoin;
this.sendGroupCallUpdateMessage(conversationId, eraId);
}
}

this.syncGroupCallToRedux(conversationId, groupCall);
Expand Down Expand Up @@ -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> {
window.log.info('CallingClass.accept()');

Expand Down
4 changes: 3 additions & 1 deletion ts/textsecure.d.ts
Expand Up @@ -653,7 +653,9 @@ export declare namespace DataMessageClass {
data?: AttachmentPointerClass;
}

class GroupCallUpdate {}
class GroupCallUpdate {
eraId?: string;
}
}

// Note: we need to use namespaces to express nested classes in Typescript
Expand Down
34 changes: 34 additions & 0 deletions ts/textsecure/SendMessage.ts
Expand Up @@ -105,6 +105,10 @@ type GroupV1InfoType = {
members: Array<string>;
};

interface GroupCallUpdateType {
eraId: string;
}

type MessageOptionsType = {
attachments?: Array<AttachmentType> | null;
body?: string;
Expand All @@ -125,6 +129,7 @@ type MessageOptionsType = {
deletedForEveryoneTimestamp?: number;
timestamp: number;
mentions?: BodyRangesType;
groupCallUpdate?: GroupCallUpdateType;
};

class Message {
Expand Down Expand Up @@ -180,6 +185,8 @@ class Message {

mentions?: BodyRangesType;

groupCallUpdate?: GroupCallUpdateType;

constructor(options: MessageOptionsType) {
this.attachments = options.attachments || [];
this.body = options.body;
Expand All @@ -197,6 +204,7 @@ class Message {
this.timestamp = options.timestamp;
this.deletedForEveryoneTimestamp = options.deletedForEveryoneTimestamp;
this.mentions = options.mentions;
this.groupCallUpdate = options.groupCallUpdate;

if (!(this.recipients instanceof Array)) {
throw new Error('Invalid recipient list');
Expand Down Expand Up @@ -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;
return proto;
}
Expand Down Expand Up @@ -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(
recipientE164: string,
recipientUuid: string,
Expand Down Expand Up @@ -1630,6 +1661,7 @@ export default class MessageSender {
deletedForEveryoneTimestamp,
timestamp,
mentions,
groupCallUpdate,
}: {
attachments?: Array<AttachmentType>;
expireTimer?: number;
Expand All @@ -1644,6 +1676,7 @@ export default class MessageSender {
deletedForEveryoneTimestamp?: number;
timestamp: number;
mentions?: BodyRangesType;
groupCallUpdate?: GroupCallUpdateType;
},
options?: SendOptionsType
): Promise<CallbackResultType> {
Expand Down Expand Up @@ -1695,6 +1728,7 @@ export default class MessageSender {
}
: undefined,
mentions,
groupCallUpdate,
};

if (recipients.length === 0) {
Expand Down

0 comments on commit b30b83e

Please sign in to comment.