Signal-Desktop/ts/types/Calling.ts

232 lines
6.5 KiB
TypeScript

// Copyright 2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import type { ReadonlyDeep } from 'type-fest';
import type { AudioDevice, Reaction as CallReaction } from '@signalapp/ringrtc';
import type { ConversationType } from '../state/ducks/conversations';
import type { AciString, ServiceIdString } from './ServiceId';
import type { CallLinkConversationType } from './CallLink';
import type { CallMode } from './CallDisposition';
export const MAX_CALLING_REACTIONS = 5;
export const CALLING_REACTIONS_LIFETIME = 4000;
// Speaker and Presentation mode have the same UI, but Presentation is only set
// automatically when someone starts to present, and will revert to the previous view mode
// once presentation is complete
export enum CallViewMode {
Paginated = 'Paginated',
Sidebar = 'Sidebar',
Speaker = 'Speaker',
Presentation = 'Presentation',
}
export type PresentableSource = ReadonlyDeep<{
appIcon?: string;
id: string;
name: string;
isScreen: boolean;
thumbnail: string;
}>;
export type PresentedSource = {
id: string;
name: string;
};
// export type ActiveCallReactionsType = {
// [timestamp: number]: ReadonlyArray<CallReaction>;
// };
export type ActiveCallReaction = {
timestamp: number;
} & CallReaction;
export type ActiveCallReactionsType = ReadonlyArray<ActiveCallReaction>;
export type ActiveCallBaseType = {
conversation: CallingConversationType;
hasLocalAudio: boolean;
hasLocalVideo: boolean;
localAudioLevel: number;
viewMode: CallViewMode;
viewModeBeforePresentation?: CallViewMode;
isSharingScreen?: boolean;
joinedAt: number | null;
outgoingRing: boolean;
pip: boolean;
presentingSource?: PresentedSource;
presentingSourcesAvailable?: ReadonlyArray<PresentableSource>;
settingsDialogOpen: boolean;
selfViewExpanded: boolean;
showNeedsScreenRecordingPermissionsWarning?: boolean;
showParticipantsList: boolean;
reactions?: ActiveCallReactionsType;
};
export type ActiveDirectCallType = ActiveCallBaseType & {
callMode: CallMode.Direct;
callState?: CallState;
callEndedReason?: CallEndedReason;
peekedParticipants: [];
remoteParticipants: [
{
hasRemoteVideo: boolean;
presenting: boolean;
title: string;
// Note that the field name/type has to match the
// GroupCallRemoteParticipantType below (which is based on
// ConversationType).
serviceId?: ServiceIdString;
},
];
hasRemoteVideo: boolean;
hasRemoteAudio: boolean;
remoteAudioLevel: number;
};
export type ObservedRemoteMuteType = {
source: number;
target: number;
};
export type ActiveGroupCallType = ActiveCallBaseType & {
callMode: CallMode.Group | CallMode.Adhoc;
connectionState: GroupCallConnectionState;
conversationsByDemuxId: ConversationsByDemuxIdType;
joinState: GroupCallJoinState;
localDemuxId: number | undefined;
maxDevices: number;
deviceCount: number;
groupMembers: Array<Pick<ConversationType, 'id' | 'firstName' | 'title'>>;
isConversationTooBigToRing: boolean;
peekedParticipants: Array<ConversationType>;
pendingParticipants: Array<ConversationType>;
raisedHands: Set<number>;
remoteParticipants: Array<GroupCallRemoteParticipantType>;
remoteAudioLevels: Map<number, number>;
suggestLowerHand: boolean;
mutedBy?: number;
observedRemoteMute?: ObservedRemoteMuteType;
};
export type ActiveCallType = ActiveDirectCallType | ActiveGroupCallType;
// Ideally, we would import many of these directly from RingRTC. But because Storybook
// cannot import RingRTC (as it runs in the browser), we have these copies. That also
// means we have to convert the "real" enum to our enum in some cases.
// Must be kept in sync with RingRTC.CallState
export enum CallState {
Prering = 'idle',
Ringing = 'ringing',
Accepted = 'connected',
Reconnecting = 'connecting',
Ended = 'ended',
}
// Must be kept in sync with RingRTC.CallEndedReason
export enum CallEndedReason {
LocalHangup = 'LocalHangup',
RemoteHangup = 'RemoteHangup',
RemoteHangupNeedPermission = 'RemoteHangupNeedPermission',
Declined = 'Declined',
Busy = 'Busy',
Glare = 'Glare',
ReCall = 'ReCall',
ReceivedOfferExpired = 'ReceivedOfferExpired',
ReceivedOfferWhileActive = 'ReceivedOfferWhileActive',
ReceivedOfferWithGlare = 'ReceivedOfferWithGlare',
SignalingFailure = 'SignalingFailure',
GlareFailure = 'GlareFailure',
ConnectionFailure = 'ConnectionFailure',
InternalFailure = 'InternalFailure',
Timeout = 'Timeout',
AcceptedOnAnotherDevice = 'AcceptedOnAnotherDevice',
DeclinedOnAnotherDevice = 'DeclinedOnAnotherDevice',
BusyOnAnotherDevice = 'BusyOnAnotherDevice',
}
// Must be kept in sync with RingRTC's ConnectionState
export enum GroupCallConnectionState {
NotConnected = 0,
Connecting = 1,
Connected = 2,
Reconnecting = 3,
}
// Must be kept in sync with RingRTC's JoinState
export enum GroupCallJoinState {
NotJoined = 0,
Joining = 1,
Pending = 2,
Joined = 3,
}
export type GroupCallRemoteParticipantType = ConversationType & {
aci: AciString;
addedTime?: number;
demuxId: number;
hasRemoteAudio: boolean;
hasRemoteVideo: boolean;
isHandRaised: boolean;
mediaKeysReceived: boolean;
presenting: boolean;
sharingScreen: boolean;
speakerTime?: number;
videoAspectRatio: number;
};
export type ConversationsByDemuxIdType = Map<number, ConversationType>;
// Similar to RingRTC's `VideoRequest` but without the `framerate` property.
export type GroupCallVideoRequest = {
demuxId: number;
width: number;
height: number;
};
export enum CallingDeviceType {
CAMERA,
MICROPHONE,
SPEAKER,
}
export type AvailableIODevicesType = {
availableCameras: Array<MediaDeviceInfo>;
availableMicrophones: Array<AudioDevice>;
availableSpeakers: Array<AudioDevice>;
};
export type MediaDeviceSettings = AvailableIODevicesType & {
selectedMicrophone: AudioDevice | undefined;
selectedSpeaker: AudioDevice | undefined;
selectedCamera: string | undefined;
};
export type ChangeIODevicePayloadType =
| { type: CallingDeviceType.CAMERA; selectedDevice: string }
| { type: CallingDeviceType.MICROPHONE; selectedDevice: AudioDevice }
| { type: CallingDeviceType.SPEAKER; selectedDevice: AudioDevice };
export type CallingConversationType =
| ConversationType
| CallLinkConversationType;
export enum ScreenShareStatus {
Connected = 'Connected',
Reconnecting = 'Reconnecting',
Disconnected = 'Disconnected',
}
export type IceServerType = {
username?: string;
password?: string;
hostname?: string;
urls: Array<string>;
};
export type IceServerCacheType = {
iceServers: Array<IceServerType>;
expirationTimestamp: number;
};