forked from Ivasoft/mattermost-mobile
Compare commits
1 Commits
test1.0.4
...
MM-50010-c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2bcea5a6a3 |
@@ -43,14 +43,13 @@ import type {
|
||||
ApiResp,
|
||||
Call,
|
||||
CallParticipant,
|
||||
CallReactionEmoji,
|
||||
CallsConnection,
|
||||
RecordingState,
|
||||
ServerCallState,
|
||||
ServerChannelState,
|
||||
} from '@calls/types/calls';
|
||||
import type {Client} from '@client/rest';
|
||||
import type ClientError from '@client/rest/error';
|
||||
import type {CallRecordingState, EmojiData} from '@mmcalls/common/lib/types';
|
||||
import type {IntlShape} from 'react-intl';
|
||||
|
||||
let connection: CallsConnection | null = null;
|
||||
@@ -322,7 +321,7 @@ export const unraiseHand = () => {
|
||||
}
|
||||
};
|
||||
|
||||
export const sendReaction = (emoji: CallReactionEmoji) => {
|
||||
export const sendReaction = (emoji: EmojiData) => {
|
||||
if (connection) {
|
||||
connection.sendReaction(emoji);
|
||||
}
|
||||
@@ -415,7 +414,7 @@ export const startCallRecording = async (serverUrl: string, callId: string) => {
|
||||
|
||||
const client = NetworkManager.getClient(serverUrl);
|
||||
|
||||
let data: ApiResp | RecordingState;
|
||||
let data: ApiResp | CallRecordingState;
|
||||
try {
|
||||
data = await client.startCallRecording(callId);
|
||||
} catch (error) {
|
||||
@@ -433,7 +432,7 @@ export const stopCallRecording = async (serverUrl: string, callId: string) => {
|
||||
|
||||
const client = NetworkManager.getClient(serverUrl);
|
||||
|
||||
let data: ApiResp | RecordingState;
|
||||
let data: ApiResp | CallRecordingState;
|
||||
try {
|
||||
data = await client.stopCallRecording(callId);
|
||||
} catch (error) {
|
||||
|
||||
@@ -1,24 +1,20 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import type {
|
||||
ServerChannelState,
|
||||
ServerCallsConfig,
|
||||
ApiResp,
|
||||
RecordingState,
|
||||
} from '@calls/types/calls';
|
||||
import type {ServerChannelState, ApiResp} from '@calls/types/calls';
|
||||
import type {CallRecordingState, CallsConfig} from '@mmcalls/common/lib/types';
|
||||
import type {RTCIceServer} from 'react-native-webrtc';
|
||||
|
||||
export interface ClientCallsMix {
|
||||
getEnabled: () => Promise<Boolean>;
|
||||
getCalls: () => Promise<ServerChannelState[]>;
|
||||
getCallForChannel: (channelId: string) => Promise<ServerChannelState>;
|
||||
getCallsConfig: () => Promise<ServerCallsConfig>;
|
||||
getCallsConfig: () => Promise<CallsConfig>;
|
||||
enableChannelCalls: (channelId: string, enable: boolean) => Promise<ServerChannelState>;
|
||||
endCall: (channelId: string) => Promise<ApiResp>;
|
||||
genTURNCredentials: () => Promise<RTCIceServer[]>;
|
||||
startCallRecording: (callId: string) => Promise<ApiResp | RecordingState>;
|
||||
stopCallRecording: (callId: string) => Promise<ApiResp | RecordingState>;
|
||||
startCallRecording: (callId: string) => Promise<ApiResp | CallRecordingState>;
|
||||
stopCallRecording: (callId: string) => Promise<ApiResp | CallRecordingState>;
|
||||
}
|
||||
|
||||
const ClientCalls = (superclass: any) => class extends superclass {
|
||||
@@ -52,7 +48,7 @@ const ClientCalls = (superclass: any) => class extends superclass {
|
||||
return this.doFetch(
|
||||
`${this.getCallsRoute()}/config`,
|
||||
{method: 'get'},
|
||||
) as ServerCallsConfig;
|
||||
) as CallsConfig;
|
||||
};
|
||||
|
||||
enableChannelCalls = async (channelId: string, enable: boolean) => {
|
||||
|
||||
@@ -8,7 +8,7 @@ import CompassIcon from '@components/compass_icon';
|
||||
import Emoji from '@components/emoji';
|
||||
import ProfilePicture from '@components/profile_picture';
|
||||
|
||||
import type {CallReactionEmoji} from '@calls/types/calls';
|
||||
import type {EmojiData} from '@mmcalls/common/lib/types';
|
||||
import type UserModel from '@typings/database/models/servers/user';
|
||||
|
||||
type Props = {
|
||||
@@ -18,7 +18,7 @@ type Props = {
|
||||
muted?: boolean;
|
||||
sharingScreen?: boolean;
|
||||
raisedHand?: boolean;
|
||||
reaction?: CallReactionEmoji;
|
||||
reaction?: EmojiData;
|
||||
size?: 'm' | 'l';
|
||||
}
|
||||
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
import {deflate} from 'pako/lib/deflate.js';
|
||||
import {RTCPeer} from '@mmcalls/common/lib';
|
||||
import {deflate} from 'pako';
|
||||
import {DeviceEventEmitter, EmitterSubscription} from 'react-native';
|
||||
import InCallManager from 'react-native-incall-manager';
|
||||
import {
|
||||
MediaStream,
|
||||
MediaStreamTrack,
|
||||
mediaDevices,
|
||||
RTCPeerConnection,
|
||||
} from 'react-native-webrtc';
|
||||
|
||||
import RTCPeer from '@calls/rtcpeer';
|
||||
import {setSpeakerPhone} from '@calls/state';
|
||||
import {getICEServersConfigs} from '@calls/utils';
|
||||
import {WebsocketEvents} from '@constants';
|
||||
@@ -22,7 +21,9 @@ import {logError, logDebug, logWarning} from '@utils/log';
|
||||
|
||||
import {WebSocketClient, wsReconnectionTimeoutErr} from './websocket_client';
|
||||
|
||||
import type {CallReactionEmoji, CallsConnection} from '@calls/types/calls';
|
||||
import type {CallsConnection} from '@calls/types/calls';
|
||||
import type {RTCPeerOpts} from '@mmcalls/common/lib/rtc_peer';
|
||||
import type {EmojiData} from '@mmcalls/common/lib/types';
|
||||
|
||||
const peerConnectTimeout = 5000;
|
||||
|
||||
@@ -164,7 +165,7 @@ export async function newConnection(
|
||||
}
|
||||
};
|
||||
|
||||
const sendReaction = (emoji: CallReactionEmoji) => {
|
||||
const sendReaction = (emoji: EmojiData) => {
|
||||
if (ws) {
|
||||
ws.send('react', {
|
||||
data: JSON.stringify(emoji),
|
||||
@@ -204,7 +205,14 @@ export async function newConnection(
|
||||
InCallManager.start({media: 'video'});
|
||||
setSpeakerPhone(true);
|
||||
|
||||
peer = new RTCPeer({iceServers: iceConfigs || []});
|
||||
const opts: RTCPeerOpts = {
|
||||
logDebug,
|
||||
webrtc: {
|
||||
MediaStream,
|
||||
RTCPeerConnection,
|
||||
},
|
||||
};
|
||||
peer = new RTCPeer({iceServers: iceConfigs || []}, opts);
|
||||
|
||||
peer.on('offer', (sdp) => {
|
||||
logDebug(`local offer, sending: ${JSON.stringify(sdp)}`);
|
||||
|
||||
@@ -1,210 +0,0 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
||||
|
||||
import {EventEmitter} from 'events';
|
||||
|
||||
import {
|
||||
MediaStream,
|
||||
MediaStreamTrack,
|
||||
RTCIceCandidate,
|
||||
RTCPeerConnection,
|
||||
RTCPeerConnectionIceEvent,
|
||||
RTCRtpSender,
|
||||
RTCSessionDescription,
|
||||
} from 'react-native-webrtc';
|
||||
|
||||
import {logDebug, logError} from '@utils/log';
|
||||
|
||||
import type {RTCPeerConfig} from './types';
|
||||
import type RTCTrackEvent from 'react-native-webrtc/lib/typescript/RTCTrackEvent';
|
||||
|
||||
const rtcConnFailedErr = new Error('rtc connection failed');
|
||||
|
||||
export default class RTCPeer extends EventEmitter {
|
||||
private pc: RTCPeerConnection | null;
|
||||
private readonly senders: { [key: string]: RTCRtpSender };
|
||||
private candidates: RTCIceCandidate[] = [];
|
||||
private makingOffer = false;
|
||||
|
||||
public connected: boolean;
|
||||
|
||||
constructor(config: RTCPeerConfig) {
|
||||
super();
|
||||
|
||||
// We keep a map of track IDs -> RTP sender so that we can easily
|
||||
// replace tracks when muting/unmuting.
|
||||
this.senders = {};
|
||||
|
||||
this.pc = new RTCPeerConnection(config);
|
||||
this.pc.onnegotiationneeded = () => this.onNegotiationNeeded();
|
||||
this.pc.onicecandidate = (ev) => this.onICECandidate(ev);
|
||||
this.pc.oniceconnectionstatechange = () => this.onICEConnectionStateChange();
|
||||
this.pc.onconnectionstatechange = () => this.onConnectionStateChange();
|
||||
this.pc.ontrack = (ev) => this.onTrack(ev);
|
||||
|
||||
this.connected = false;
|
||||
|
||||
// We create a data channel for two reasons:
|
||||
// - Initiate a connection without preemptively adding audio/video tracks.
|
||||
// - Use this communication channel for further negotiation (to be implemented).
|
||||
this.pc.createDataChannel('calls-dc');
|
||||
}
|
||||
|
||||
private onICECandidate(ev: RTCPeerConnectionIceEvent) {
|
||||
if (ev.candidate) {
|
||||
this.emit('candidate', ev.candidate);
|
||||
}
|
||||
}
|
||||
|
||||
private onConnectionStateChange() {
|
||||
switch (this.pc?.connectionState) {
|
||||
case 'connected':
|
||||
this.connected = true;
|
||||
break;
|
||||
case 'failed':
|
||||
this.emit('close', rtcConnFailedErr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private onICEConnectionStateChange() {
|
||||
switch (this.pc?.iceConnectionState) {
|
||||
case 'connected':
|
||||
this.emit('connect');
|
||||
break;
|
||||
case 'failed':
|
||||
this.emit('close', rtcConnFailedErr);
|
||||
break;
|
||||
case 'closed':
|
||||
this.emit('close');
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
private async onNegotiationNeeded() {
|
||||
try {
|
||||
this.makingOffer = true;
|
||||
await this.pc?.setLocalDescription();
|
||||
this.emit('offer', this.pc?.localDescription);
|
||||
} catch (err) {
|
||||
this.emit('error', err);
|
||||
} finally {
|
||||
this.makingOffer = false;
|
||||
}
|
||||
}
|
||||
|
||||
private onTrack(ev: RTCTrackEvent) {
|
||||
if (ev.streams.length === 0) {
|
||||
this.emit('stream', new MediaStream([ev.track]));
|
||||
return;
|
||||
}
|
||||
this.emit('stream', ev.streams[0]);
|
||||
}
|
||||
|
||||
public async signal(data: string) {
|
||||
if (!this.pc) {
|
||||
throw new Error('peer has been destroyed');
|
||||
}
|
||||
|
||||
const msg = JSON.parse(data);
|
||||
|
||||
if (msg.type === 'offer' && (this.makingOffer || this.pc?.signalingState !== 'stable')) {
|
||||
logDebug('signaling conflict, we are polite, proceeding...');
|
||||
}
|
||||
|
||||
try {
|
||||
switch (msg.type) {
|
||||
case 'candidate':
|
||||
// It's possible that ICE candidates are received moments before
|
||||
// we set the initial remote description which would cause an
|
||||
// error. In such case we queue them up to be added later.
|
||||
if (this.pc.remoteDescription && this.pc.remoteDescription.type) {
|
||||
this.pc.addIceCandidate(msg.candidate).catch((err) => {
|
||||
logError('failed to add candidate', err);
|
||||
});
|
||||
} else {
|
||||
logDebug('received ice candidate before remote description, queuing...');
|
||||
this.candidates.push(msg.candidate);
|
||||
}
|
||||
break;
|
||||
case 'offer':
|
||||
await this.pc.setRemoteDescription(new RTCSessionDescription(msg));
|
||||
await this.pc.setLocalDescription();
|
||||
this.emit('answer', this.pc.localDescription);
|
||||
break;
|
||||
case 'answer':
|
||||
await this.pc.setRemoteDescription(msg);
|
||||
for (const candidate of this.candidates) {
|
||||
logDebug('adding queued ice candidate');
|
||||
this.pc.addIceCandidate(candidate).catch((err) => {
|
||||
logError('failed to add candidate', err);
|
||||
});
|
||||
}
|
||||
break;
|
||||
default:
|
||||
this.emit('error', Error('invalid signaling data received'));
|
||||
}
|
||||
} catch (err) {
|
||||
this.emit('error', err);
|
||||
}
|
||||
}
|
||||
|
||||
public async addTrack(track: MediaStreamTrack, stream?: MediaStream) {
|
||||
if (!this.pc) {
|
||||
throw new Error('peer has been destroyed');
|
||||
}
|
||||
const sender = await this.pc.addTrack(track, stream!);
|
||||
if (sender) {
|
||||
this.senders[track.id] = sender;
|
||||
}
|
||||
}
|
||||
|
||||
public addStream(stream: MediaStream) {
|
||||
stream.getTracks().forEach((track) => {
|
||||
this.addTrack(track, stream);
|
||||
});
|
||||
}
|
||||
|
||||
public replaceTrack(oldTrackID: string, newTrack: MediaStreamTrack | null) {
|
||||
const sender = this.senders[oldTrackID];
|
||||
if (!sender) {
|
||||
throw new Error('sender for track not found');
|
||||
}
|
||||
if (newTrack && newTrack.id !== oldTrackID) {
|
||||
delete this.senders[oldTrackID];
|
||||
this.senders[newTrack.id] = sender;
|
||||
}
|
||||
sender.replaceTrack(newTrack);
|
||||
}
|
||||
|
||||
public getStats() {
|
||||
if (!this.pc) {
|
||||
throw new Error('peer has been destroyed');
|
||||
}
|
||||
return this.pc.getStats();
|
||||
}
|
||||
|
||||
public destroy() {
|
||||
if (!this.pc) {
|
||||
throw new Error('peer has been destroyed already');
|
||||
}
|
||||
|
||||
this.removeAllListeners('candidate');
|
||||
this.removeAllListeners('connect');
|
||||
this.removeAllListeners('error');
|
||||
this.removeAllListeners('close');
|
||||
this.removeAllListeners('offer');
|
||||
this.removeAllListeners('answer');
|
||||
this.removeAllListeners('stream');
|
||||
this.pc.onnegotiationneeded = null;
|
||||
this.pc.onicecandidate = null;
|
||||
this.pc.oniceconnectionstatechange = null;
|
||||
this.pc.onconnectionstatechange = null;
|
||||
this.pc.ontrack = null;
|
||||
this.pc.close();
|
||||
this.pc = null;
|
||||
this.connected = false;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import type {RTCIceServer} from 'react-native-webrtc';
|
||||
|
||||
export type RTCPeerConfig = {
|
||||
iceServers: RTCIceServer[];
|
||||
}
|
||||
@@ -40,8 +40,6 @@ import {
|
||||
setPluginEnabled,
|
||||
setUserVoiceOn,
|
||||
} from '@calls/state/actions';
|
||||
import {License} from '@constants';
|
||||
|
||||
import {
|
||||
Call,
|
||||
CallsState,
|
||||
@@ -51,8 +49,10 @@ import {
|
||||
DefaultCurrentCall,
|
||||
DefaultGlobalCallsState,
|
||||
GlobalCallsState,
|
||||
RecordingState,
|
||||
} from '../types/calls';
|
||||
} from '@calls/types/calls';
|
||||
import {License} from '@constants';
|
||||
|
||||
import type {CallRecordingState} from '@mmcalls/common/lib/types';
|
||||
|
||||
jest.mock('@calls/alerts');
|
||||
|
||||
@@ -797,6 +797,7 @@ describe('useCallsState', () => {
|
||||
|
||||
it('config', () => {
|
||||
const newConfig = {
|
||||
...DefaultCallsConfig,
|
||||
ICEServers: [],
|
||||
ICEServersConfigs: [
|
||||
{
|
||||
@@ -914,7 +915,7 @@ describe('useCallsState', () => {
|
||||
myUserId: 'myUserId',
|
||||
...call1,
|
||||
};
|
||||
const recState: RecordingState = {
|
||||
const recState: CallRecordingState = {
|
||||
init_at: 123,
|
||||
start_at: 231,
|
||||
end_at: 345,
|
||||
|
||||
@@ -16,17 +16,17 @@ import {
|
||||
} from '@calls/state';
|
||||
import {
|
||||
Call,
|
||||
CallReaction,
|
||||
CallsConfig,
|
||||
CallsConfigState,
|
||||
ChannelsWithCalls,
|
||||
CurrentCall,
|
||||
DefaultCall,
|
||||
DefaultCurrentCall,
|
||||
ReactionStreamEmoji,
|
||||
RecordingState,
|
||||
} from '@calls/types/calls';
|
||||
import {REACTION_LIMIT, REACTION_TIMEOUT} from '@constants/calls';
|
||||
|
||||
import type {CallRecordingState, UserReactionData} from '@mmcalls/common/lib/types';
|
||||
|
||||
export const setCalls = (serverUrl: string, myUserId: string, calls: Dictionary<Call>, enabled: Dictionary<boolean>) => {
|
||||
const channelsWithCalls = Object.keys(calls).reduce(
|
||||
(accum, next) => {
|
||||
@@ -390,7 +390,7 @@ export const setSpeakerPhone = (speakerphoneOn: boolean) => {
|
||||
}
|
||||
};
|
||||
|
||||
export const setConfig = (serverUrl: string, config: Partial<CallsConfig>) => {
|
||||
export const setConfig = (serverUrl: string, config: Partial<CallsConfigState>) => {
|
||||
const callsConfig = getCallsConfig(serverUrl);
|
||||
setCallsConfig(serverUrl, {...callsConfig, ...config});
|
||||
};
|
||||
@@ -423,7 +423,7 @@ export const setMicPermissionsErrorDismissed = () => {
|
||||
setCurrentCall(nextCurrentCall);
|
||||
};
|
||||
|
||||
export const userReacted = (serverUrl: string, channelId: string, reaction: CallReaction) => {
|
||||
export const userReacted = (serverUrl: string, channelId: string, reaction: UserReactionData) => {
|
||||
// Note: Simplification for performance:
|
||||
// If you are not in the call with the reaction, ignore it. There could be many calls ongoing in your
|
||||
// servers, do we want to be tracking reactions and setting timeouts for all those calls? No.
|
||||
@@ -474,7 +474,7 @@ export const userReacted = (serverUrl: string, channelId: string, reaction: Call
|
||||
}, REACTION_TIMEOUT);
|
||||
};
|
||||
|
||||
const userReactionTimeout = (serverUrl: string, channelId: string, reaction: CallReaction) => {
|
||||
const userReactionTimeout = (serverUrl: string, channelId: string, reaction: UserReactionData) => {
|
||||
const currentCall = getCurrentCall();
|
||||
if (currentCall?.channelId !== channelId) {
|
||||
return;
|
||||
@@ -498,7 +498,7 @@ const userReactionTimeout = (serverUrl: string, channelId: string, reaction: Cal
|
||||
setCurrentCall(nextCurrentCall);
|
||||
};
|
||||
|
||||
export const setRecordingState = (serverUrl: string, channelId: string, recState: RecordingState) => {
|
||||
export const setRecordingState = (serverUrl: string, channelId: string, recState: CallRecordingState) => {
|
||||
const callsState = getCallsState(serverUrl);
|
||||
if (!callsState.calls[channelId]) {
|
||||
return;
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
import {useEffect, useState} from 'react';
|
||||
import {BehaviorSubject} from 'rxjs';
|
||||
|
||||
import {CallsConfig, DefaultCallsConfig} from '@calls/types/calls';
|
||||
import {CallsConfigState, DefaultCallsConfig} from '@calls/types/calls';
|
||||
|
||||
const callsConfigSubjects: Dictionary<BehaviorSubject<CallsConfig>> = {};
|
||||
const callsConfigSubjects: Dictionary<BehaviorSubject<CallsConfigState>> = {};
|
||||
|
||||
const getCallsConfigSubject = (serverUrl: string) => {
|
||||
if (!callsConfigSubjects[serverUrl]) {
|
||||
@@ -20,7 +20,7 @@ export const getCallsConfig = (serverUrl: string) => {
|
||||
return getCallsConfigSubject(serverUrl).value;
|
||||
};
|
||||
|
||||
export const setCallsConfig = (serverUrl: string, callsConfig: CallsConfig) => {
|
||||
export const setCallsConfig = (serverUrl: string, callsConfig: CallsConfigState) => {
|
||||
getCallsConfigSubject(serverUrl).next(callsConfig);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import type {CallRecordingState, CallsConfig, EmojiData, UserReactionData} from '@mmcalls/common/lib/types';
|
||||
import type UserModel from '@typings/database/models/servers/user';
|
||||
import type {RTCIceServer} from 'react-native-webrtc';
|
||||
|
||||
export type GlobalCallsState = {
|
||||
micPermissionsGranted: boolean;
|
||||
@@ -31,7 +31,7 @@ export type Call = {
|
||||
screenOn: string;
|
||||
threadId: string;
|
||||
ownerId: string;
|
||||
recState?: RecordingState;
|
||||
recState?: CallRecordingState;
|
||||
hostId: string;
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ export type CallParticipant = {
|
||||
muted: boolean;
|
||||
raisedHand: number;
|
||||
userModel?: UserModel;
|
||||
reaction?: CallReaction;
|
||||
reaction?: UserReactionData;
|
||||
}
|
||||
|
||||
export type ChannelsWithCalls = Dictionary<boolean>;
|
||||
@@ -100,7 +100,7 @@ export type ServerCallState = {
|
||||
screen_sharing_id: string;
|
||||
owner_id: string;
|
||||
host_id: string;
|
||||
recording: RecordingState;
|
||||
recording: CallRecordingState;
|
||||
}
|
||||
|
||||
export type CallsConnection = {
|
||||
@@ -111,26 +111,16 @@ export type CallsConnection = {
|
||||
raiseHand: () => void;
|
||||
unraiseHand: () => void;
|
||||
initializeVoiceTrack: () => void;
|
||||
sendReaction: (emoji: CallReactionEmoji) => void;
|
||||
sendReaction: (emoji: EmojiData) => void;
|
||||
}
|
||||
|
||||
export type ServerCallsConfig = {
|
||||
ICEServers?: string[]; // deprecated
|
||||
ICEServersConfigs: RTCIceServer[];
|
||||
export type CallsConfigState = CallsConfig & {
|
||||
AllowEnableCalls: boolean;
|
||||
DefaultEnabled: boolean;
|
||||
NeedsTURNCredentials: boolean;
|
||||
sku_short_name: string;
|
||||
MaxCallParticipants: number;
|
||||
EnableRecordings: boolean;
|
||||
}
|
||||
|
||||
export type CallsConfig = ServerCallsConfig & {
|
||||
pluginEnabled: boolean;
|
||||
last_retrieved_at: number;
|
||||
}
|
||||
|
||||
export const DefaultCallsConfig: CallsConfig = {
|
||||
export const DefaultCallsConfig: CallsConfigState = {
|
||||
pluginEnabled: false,
|
||||
ICEServers: [], // deprecated
|
||||
ICEServersConfigs: [],
|
||||
@@ -141,6 +131,8 @@ export const DefaultCallsConfig: CallsConfig = {
|
||||
sku_short_name: '',
|
||||
MaxCallParticipants: 0,
|
||||
EnableRecordings: false,
|
||||
MaxRecordingDuration: 60,
|
||||
AllowScreenSharing: true,
|
||||
};
|
||||
|
||||
export type ApiResp = {
|
||||
@@ -149,18 +141,6 @@ export type ApiResp = {
|
||||
status_code: number;
|
||||
}
|
||||
|
||||
export type CallReactionEmoji = {
|
||||
name: string;
|
||||
skin?: string;
|
||||
unified: string;
|
||||
}
|
||||
|
||||
export type CallReaction = {
|
||||
user_id: string;
|
||||
emoji: CallReactionEmoji;
|
||||
timestamp: number;
|
||||
}
|
||||
|
||||
export type ReactionStreamEmoji = {
|
||||
name: string;
|
||||
latestTimestamp: number;
|
||||
|
||||
@@ -3,15 +3,15 @@
|
||||
|
||||
import assert from 'assert';
|
||||
|
||||
import {CallsConfigState, DefaultCallsConfig} from '@calls/types/calls';
|
||||
import {License} from '@constants';
|
||||
|
||||
import {getICEServersConfigs} from './utils';
|
||||
|
||||
import type {CallsConfig} from '@calls/types/calls';
|
||||
|
||||
describe('getICEServersConfigs', () => {
|
||||
it('backwards compatible case, no ICEServersConfigs present', () => {
|
||||
const config: CallsConfig = {
|
||||
const config: CallsConfigState = {
|
||||
...DefaultCallsConfig,
|
||||
pluginEnabled: true,
|
||||
ICEServers: ['stun:stun.example.com:3478'],
|
||||
ICEServersConfigs: [],
|
||||
@@ -33,7 +33,8 @@ describe('getICEServersConfigs', () => {
|
||||
});
|
||||
|
||||
it('ICEServersConfigs set', () => {
|
||||
const config: CallsConfig = {
|
||||
const config: CallsConfigState = {
|
||||
...DefaultCallsConfig,
|
||||
pluginEnabled: true,
|
||||
ICEServersConfigs: [
|
||||
{
|
||||
@@ -64,7 +65,8 @@ describe('getICEServersConfigs', () => {
|
||||
});
|
||||
|
||||
it('Both ICEServers and ICEServersConfigs set', () => {
|
||||
const config: CallsConfig = {
|
||||
const config: CallsConfigState = {
|
||||
...DefaultCallsConfig,
|
||||
pluginEnabled: true,
|
||||
ICEServers: ['stun:stuna.example.com:3478'],
|
||||
ICEServersConfigs: [
|
||||
|
||||
@@ -8,9 +8,11 @@ import Calls from '@constants/calls';
|
||||
import {isMinimumServerVersion} from '@utils/helpers';
|
||||
import {displayUsername} from '@utils/user';
|
||||
|
||||
import type {CallParticipant, ServerCallsConfig} from '@calls/types/calls';
|
||||
import type {CallParticipant} from '@calls/types/calls';
|
||||
import type {CallsConfig} from '@mmcalls/common/lib/types';
|
||||
import type PostModel from '@typings/database/models/servers/post';
|
||||
import type {IntlShape} from 'react-intl';
|
||||
import type {RTCIceServer} from 'react-native-webrtc';
|
||||
|
||||
export function sortParticipants(teammateNameDisplay: string, participants?: Dictionary<CallParticipant>, presenterID?: string): CallParticipant[] {
|
||||
if (!participants) {
|
||||
@@ -106,7 +108,7 @@ export function errorAlert(error: string, intl: IntlShape) {
|
||||
);
|
||||
}
|
||||
|
||||
export function getICEServersConfigs(config: ServerCallsConfig) {
|
||||
export function getICEServersConfigs(config: CallsConfig): RTCIceServer[] {
|
||||
// if ICEServersConfigs is set, we can trust this to be complete and
|
||||
// coming from an updated API.
|
||||
if (config.ICEServersConfigs && config.ICEServersConfigs.length > 0) {
|
||||
|
||||
@@ -19,6 +19,6 @@ module.exports = {
|
||||
'\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': '<rootDir>/test/file_transformer.js',
|
||||
},
|
||||
transformIgnorePatterns: [
|
||||
'node_modules/(?!(@react-native|react-native)|jail-monkey|@sentry/react-native|react-clone-referenced-element|@react-native-community|react-navigation|@react-navigation/.*|validator|react-syntax-highlighter/.*|hast-util-from-selector|hastscript|property-information|hast-util-parse-selector|space-separated-tokens|comma-separated-tokens|zwitch)',
|
||||
'node_modules/(?!(@react-native|react-native)|jail-monkey|@sentry/react-native|react-clone-referenced-element|@react-native-community|react-navigation|@react-navigation/.*|validator|react-syntax-highlighter/.*|hast-util-from-selector|hastscript|property-information|hast-util-parse-selector|space-separated-tokens|comma-separated-tokens|zwitch|@mmcalls/common)',
|
||||
],
|
||||
};
|
||||
|
||||
25
package-lock.json
generated
25
package-lock.json
generated
@@ -23,6 +23,7 @@
|
||||
"@mattermost/react-native-paste-input": "0.6.2",
|
||||
"@mattermost/react-native-turbo-log": "0.2.3",
|
||||
"@mattermost/react-native-turbo-mailer": "0.2.4",
|
||||
"@mmcalls/common": "https://gitpkg.now.sh/mattermost/mattermost-plugin-calls/webapp/packages/common?b5e89f3",
|
||||
"@msgpack/msgpack": "2.8.0",
|
||||
"@nozbe/watermelondb": "0.25.5",
|
||||
"@nozbe/with-observables": "1.4.1",
|
||||
@@ -124,6 +125,7 @@
|
||||
"@types/jest": "29.4.0",
|
||||
"@types/lodash": "4.14.191",
|
||||
"@types/mime-db": "1.43.1",
|
||||
"@types/pako": "2.0.0",
|
||||
"@types/querystringify": "2.0.0",
|
||||
"@types/react": "18.0.28",
|
||||
"@types/react-native-background-timer": "2.0.0",
|
||||
@@ -3286,6 +3288,13 @@
|
||||
"react-native": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@mmcalls/common": {
|
||||
"name": "@calls/common",
|
||||
"version": "0.12.0",
|
||||
"resolved": "https://gitpkg.now.sh/mattermost/mattermost-plugin-calls/webapp/packages/common?b5e89f3",
|
||||
"integrity": "sha512-lz+T2HHQBfQDpgrI4CkrSNkmBjIaKxJGSxSZHSL0LQ8xAlRUBo3WMG5wJlwwzpxP6rixJjUnoyKBSo8Jn0wWQQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@msgpack/msgpack": {
|
||||
"version": "2.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@msgpack/msgpack/-/msgpack-2.8.0.tgz",
|
||||
@@ -5957,6 +5966,12 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.1.tgz",
|
||||
"integrity": "sha512-PYGcJHL9mwl1Ek3PLiYgyEKtwTMmkMw4vbiyz/ps3pfdRYLVv+SN7qHVAImrjdAXxgluDEw6Ph4lyv+m9UpRmA=="
|
||||
},
|
||||
"node_modules/@types/pako": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/pako/-/pako-2.0.0.tgz",
|
||||
"integrity": "sha512-10+iaz93qR5WYxTo+PMifD5TSxiOtdRaxBf7INGGXMQgTCu8Z/7GYWYFUOS3q/G0nE5boj1r4FEB+WSy7s5gbA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/parse-json": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
|
||||
@@ -24325,6 +24340,10 @@
|
||||
"integrity": "sha512-6W37UvLxg7vhP5YJXZfzKvPy4r9bozqSSuB4gbC2EjvWrGB4LfwKWljgw+Gb/E8x3ceMCib2SPPMz+thzs7DHw==",
|
||||
"requires": {}
|
||||
},
|
||||
"@mmcalls/common": {
|
||||
"version": "https://gitpkg.now.sh/mattermost/mattermost-plugin-calls/webapp/packages/common?b5e89f3",
|
||||
"integrity": "sha512-lz+T2HHQBfQDpgrI4CkrSNkmBjIaKxJGSxSZHSL0LQ8xAlRUBo3WMG5wJlwwzpxP6rixJjUnoyKBSo8Jn0wWQQ=="
|
||||
},
|
||||
"@msgpack/msgpack": {
|
||||
"version": "2.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@msgpack/msgpack/-/msgpack-2.8.0.tgz",
|
||||
@@ -26328,6 +26347,12 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.1.tgz",
|
||||
"integrity": "sha512-PYGcJHL9mwl1Ek3PLiYgyEKtwTMmkMw4vbiyz/ps3pfdRYLVv+SN7qHVAImrjdAXxgluDEw6Ph4lyv+m9UpRmA=="
|
||||
},
|
||||
"@types/pako": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/pako/-/pako-2.0.0.tgz",
|
||||
"integrity": "sha512-10+iaz93qR5WYxTo+PMifD5TSxiOtdRaxBf7INGGXMQgTCu8Z/7GYWYFUOS3q/G0nE5boj1r4FEB+WSy7s5gbA==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/parse-json": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
"@mattermost/react-native-paste-input": "0.6.2",
|
||||
"@mattermost/react-native-turbo-log": "0.2.3",
|
||||
"@mattermost/react-native-turbo-mailer": "0.2.4",
|
||||
"@mmcalls/common": "https://gitpkg.now.sh/mattermost/mattermost-plugin-calls/webapp/packages/common?b5e89f3",
|
||||
"@msgpack/msgpack": "2.8.0",
|
||||
"@nozbe/watermelondb": "0.25.5",
|
||||
"@nozbe/with-observables": "1.4.1",
|
||||
@@ -121,6 +122,7 @@
|
||||
"@types/jest": "29.4.0",
|
||||
"@types/lodash": "4.14.191",
|
||||
"@types/mime-db": "1.43.1",
|
||||
"@types/pako": "2.0.0",
|
||||
"@types/querystringify": "2.0.0",
|
||||
"@types/react": "18.0.28",
|
||||
"@types/react-native-background-timer": "2.0.0",
|
||||
|
||||
Reference in New Issue
Block a user