diff --git a/app/components/CallOverlay.js b/app/components/CallOverlay.js index 7f1ba6d..42fd5fa 100644 --- a/app/components/CallOverlay.js +++ b/app/components/CallOverlay.js @@ -1,113 +1,122 @@ import React from 'react'; import { View, Text } from 'react-native'; import PropTypes from 'prop-types'; import classNames from 'classnames'; import moment from 'moment'; import momentFormat from 'moment-duration-format'; import autoBind from 'auto-bind'; import { Appbar } from 'react-native-paper'; import Icon from 'react-native-vector-icons/MaterialCommunityIcons'; class CallOverlay extends React.Component { constructor(props) { super(props); autoBind(this); this.duration = null; this.timer = null; this._isMounted = true; } componentDidMount() { if (this.props.call) { if (this.props.call.state === 'established') { this.startTimer(); } else if (this.props.call.state !== 'terminated') { this.props.call.on('stateChanged', this.callStateChanged); } } } componentWillReceiveProps(nextProps) { if (this.props.call == null && nextProps.call) { if (nextProps.call.state === 'established') { this.startTimer(); } else if (nextProps.call.state !== 'terminated') { nextProps.call.on('stateChanged', this.callStateChanged); } } } componentWillUnmount() { this._isMounted = false; clearTimeout(this.timer); } callStateChanged(oldState, newState, data) { // Prevent starting timer when we are unmounted if (newState === 'established' && this._isMounted) { this.startTimer(); this.props.call.removeListener('stateChanged', this.callStateChanged); } } startTimer() { if (this.timer !== null) { // already armed return; } // TODO: consider using window.requestAnimationFrame const startTime = new Date(); this.timer = setInterval(() => { this.duration = moment.duration(new Date() - startTime).format('hh:mm:ss', {trim: false}); if (this.props.show) { this.forceUpdate(); } }, 300); } render() { let header = null; let displayName = this.props.remoteUri; console.log('uri', this.props.remoteUri); if (this.props.remoteDisplayName && this.props.remoteDisplayName !== this.props.remoteUri) { displayName = this.props.remoteDisplayName; } if (this.props.show) { let callDetail; if (this.duration !== null) { callDetail = {this.duration}; callDetail = 'Duration:' + this.duration; } else { callDetail = 'Connecting...' } - header = ( - - - - ); + if (this.props.remoteUri.search('videoconference') > -1) { + header = ( + + + + ); + } else { + header = ( + + + + ); + } } return header } } CallOverlay.propTypes = { show: PropTypes.bool.isRequired, remoteUri: PropTypes.string.isRequired, remoteDisplayName: PropTypes.string.isRequired, call: PropTypes.object }; export default CallOverlay; diff --git a/app/components/Conference.js b/app/components/Conference.js index 9a43b5f..e7358c5 100644 --- a/app/components/Conference.js +++ b/app/components/Conference.js @@ -1,109 +1,109 @@ import React from 'react'; import { View } from 'react-native'; import PropTypes from 'prop-types'; import assert from 'assert'; import debug from 'react-native-debug'; import autoBind from 'auto-bind'; import ConferenceBox from './ConferenceBox'; import LocalMedia from './LocalMedia'; import config from '../config'; const DEBUG = debug('blinkrtc:Conference'); debug.enable('*'); class Conference extends React.Component { constructor(props) { super(props); autoBind(this); } confStateChanged(oldState, newState, data) { DEBUG(`Conference state changed ${oldState} -> ${newState}`); if (newState === 'established') { this.forceUpdate(); } } start() { if (this.props.currentCall === null) { const options = { id: this.props.callUUID, pcConfig: {iceServers: config.iceServers}, localStream: this.props.localMedia, audio: true, video: true, offerOptions: { offerToReceiveAudio: false, offerToReceiveVideo: false }, initialParticipants: this.props.participantsToInvite }; console.log('Starting conference call', this.props.callUUID, 'to', this.props.targetUri.toLowerCase(), options); const confCall = this.props.account.joinConference(this.props.targetUri.toLowerCase(), options); confCall.on('stateChanged', this.confStateChanged); } else { console.log('Cannot start conference, there is already a call in progress'); } } hangup() { this.props.hangupCall(this.props.callUUID); } mediaPlaying() { if (this.props.currentCall === null) { this.start(); } else { DEBUG('CALL ALREADY STARTED'); } } render() { let box = null; if (this.props.localMedia !== null) { if (this.props.currentCall != null && this.props.currentCall.state === 'established') { box = ( ); } else { box = ( ); } } return box; } } Conference.propTypes = { notificationCenter : PropTypes.func.isRequired, account : PropTypes.object.isRequired, hangupCall : PropTypes.func.isRequired, currentCall : PropTypes.object, localMedia : PropTypes.object, targetUri : PropTypes.string, participantsToInvite : PropTypes.array, generatedVideoTrack : PropTypes.bool, callUUID : PropTypes.string }; export default Conference; diff --git a/app/components/ConferenceBox.js b/app/components/ConferenceBox.js index 6166b43..a18b25e 100644 --- a/app/components/ConferenceBox.js +++ b/app/components/ConferenceBox.js @@ -1,717 +1,717 @@ 'use strict'; import React, {Component, Fragment} from 'react'; import { View, Platform } from 'react-native'; import PropTypes from 'prop-types'; import * as sylkrtc from 'sylkrtc'; import classNames from 'classnames'; import debug from 'react-native-debug'; import superagent from 'superagent'; import autoBind from 'auto-bind'; import { RTCView } from 'react-native-webrtc'; import { IconButton, Appbar, Portal, Modal, Surface, Paragraph } from 'react-native-paper'; import config from '../config'; import utils from '../utils'; //import AudioPlayer from './AudioPlayer'; import ConferenceDrawer from './ConferenceDrawer'; import ConferenceDrawerLog from './ConferenceDrawerLog'; // import ConferenceDrawerFiles from './ConferenceDrawerFiles'; import ConferenceDrawerParticipant from './ConferenceDrawerParticipant'; import ConferenceDrawerParticipantList from './ConferenceDrawerParticipantList'; import ConferenceDrawerSpeakerSelection from './ConferenceDrawerSpeakerSelection'; import ConferenceHeader from './ConferenceHeader'; import ConferenceCarousel from './ConferenceCarousel'; import ConferenceParticipant from './ConferenceParticipant'; import ConferenceMatrixParticipant from './ConferenceMatrixParticipant'; import ConferenceParticipantSelf from './ConferenceParticipantSelf'; import InviteParticipantsModal from './InviteParticipantsModal'; import styles from '../assets/styles/blink/_ConferenceBox.scss'; const DEBUG = debug('blinkrtc:ConferenceBox'); debug.enable('*'); class ConferenceBox extends Component { constructor(props) { super(props); autoBind(this); this.state = { callOverlayVisible: true, audioMuted: false, videoMuted: false, participants: props.call.participants.slice(), showInviteModal: false, showDrawer: false, showFiles: false, shareOverlayVisible: false, activeSpeakers: props.call.activeParticipants.slice(), selfDisplayedLarge: false, eventLog: [], sharedFiles: props.call.sharedFiles.slice(), largeVideoStream: null }; - const friendlyName = this.props.remoteIdentity.split('@')[0]; + const friendlyName = this.props.remoteUri.split('@')[0]; //if (window.location.origin.startsWith('file://')) { this.callUrl = `${config.publicUrl}/conference/${friendlyName}`; //} else { // this.callUrl = `${window.location.origin}/conference/${friendlyName}`; //} const emailMessage = `You can join me in the conference using a Web browser at ${this.callUrl} ` + 'or by using the freely available Sylk WebRTC client app at http://sylkserver.com'; const subject = 'Join me, maybe?'; this.emailLink = `mailto:?subject=${encodeURI(subject)}&body=${encodeURI(emailMessage)}`; this.overlayTimer = null; this.logEvent = {}; this.haveVideo = false; this.uploads = []; [ 'error', 'warning', 'info', 'debug' ].forEach((level) => { this.logEvent[level] = ( (action, messages, originator) => { const log = this.state.eventLog.slice(); log.unshift({originator, originator, level: level, action: action, messages: messages}); this.setState({eventLog: log}); } ); }); } componentDidMount() { for (let p of this.state.participants) { p.on('stateChanged', this.onParticipantStateChanged); p.attach(); } this.props.call.on('participantJoined', this.onParticipantJoined); this.props.call.on('participantLeft', this.onParticipantLeft); this.props.call.on('roomConfigured', this.onConfigureRoom); this.props.call.on('fileSharing', this.onFileSharing); this.armOverlayTimer(); // attach to ourselves first if there are no other participants if (this.state.participants.length === 0) { setTimeout(() => { const item = { stream: this.props.call.getLocalStreams()[0], identity: this.props.call.localIdentity }; this.selectVideo(item); }); } else { // this.changeResolution(); } if (this.props.call.getLocalStreams()[0].getVideoTracks().length !== 0) { this.haveVideo = true; } } componentWillUnmount() { clearTimeout(this.overlayTimer); this.uploads.forEach((upload) => { this.props.notificationCenter().removeNotification(upload[1]); upload[0].abort(); }) } onParticipantJoined(p) { DEBUG(`Participant joined: ${p.identity}`); // this.refs.audioPlayerParticipantJoined.play(); p.on('stateChanged', this.onParticipantStateChanged); p.attach(); this.setState({ participants: this.state.participants.concat([p]) }); // this.changeResolution(); } onParticipantLeft(p) { DEBUG(`Participant left: ${p.identity}`); // this.refs.audioPlayerParticipantLeft.play(); const participants = this.state.participants.slice(); const idx = participants.indexOf(p); if (idx !== -1) { participants.splice(idx, 1); this.setState({ participants: participants }); } p.detach(true); // this.changeResolution(); } onParticipantStateChanged(oldState, newState) { if (newState === 'established' || newState === null) { this.maybeSwitchLargeVideo(); } } onConfigureRoom(config) { const newState = {}; newState.activeSpeakers = config.activeParticipants; this.setState(newState); if (config.activeParticipants.length === 0) { this.logEvent.info('set speakers to', ['Nobody'], config.originator); } else { const speakers = config.activeParticipants.map((p) => {return p.identity.displayName || p.identity.uri}); this.logEvent.info('set speakers to', speakers, config.originator); } this.maybeSwitchLargeVideo(); } onFileSharing(files) { let stateFiles = this.state.sharedFiles.slice(); stateFiles = stateFiles.concat(files); this.setState({sharedFiles: stateFiles}); files.forEach((file)=>{ if (file.session !== this.props.call.id) { this.props.notificationCenter().postFileShared(file, this.showFiles); } }) } changeResolution() { let stream = this.props.call.getLocalStreams()[0]; if (this.state.participants.length < 2) { this.props.call.scaleLocalTrack(stream, 1.5); } else if (this.state.participants.length < 5) { this.props.call.scaleLocalTrack(stream, 2); } else { this.props.call.scaleLocalTrack(stream, 1); } } selectVideo(item) { DEBUG('Switching video to: %o', item); if (item.stream) { this.setState({selfDisplayedLarge: true, largeVideoStream: item.stream}); } } maybeSwitchLargeVideo() { // Switch the large video to another source, maybe. if (this.state.participants.length === 0 && !this.state.selfDisplayedLarge) { // none of the participants are eligible, show ourselves const item = { stream: this.props.call.getLocalStreams()[0], identity: this.props.call.localIdentity }; this.selectVideo(item); } else if (this.state.selfDisplayedLarge) { this.setState({selfDisplayedLarge: false}); } } handleClipboardButton() { utils.copyToClipboard(this.callUrl); this.props.notificationCenter().postSystemNotification('Join me, maybe?', {body: 'Link copied to the clipboard'}); this.setState({shareOverlayVisible: false}); } handleEmailButton(event) { // if (navigator.userAgent.indexOf('Chrome') > 0) { // let emailWindow = window.open(this.emailLink, '_blank'); // setTimeout(() => { // emailWindow.close(); // }, 500); // } else { // window.open(this.emailLink, '_self'); // } this.setState({shareOverlayVisible: false}); } handleShareOverlayEntered() { this.setState({shareOverlayVisible: true}); } handleShareOverlayExited() { this.setState({shareOverlayVisible: false}); } handleActiveSpeakerSelected(participant, secondVideo=false) { // eslint-disable-line space-infix-ops let newActiveSpeakers = this.state.activeSpeakers.slice(); if (secondVideo) { if (participant.id !== 'none') { if (newActiveSpeakers.length >= 1) { newActiveSpeakers[1] = participant; } else { newActiveSpeakers[0] = participant; } } else { newActiveSpeakers.splice(1,1); } } else { if (participant.id !== 'none') { newActiveSpeakers[0] = participant; } else { newActiveSpeakers.shift(); } } this.props.call.configureRoom(newActiveSpeakers.map((element) => element.publisherId), (error) => { if (error) { // This causes a state update, hence the drawer lists update this.logEvent.error('set speakers failed', [], this.localIdentity); } }); } handleDrop(files) { DEBUG('Dropped file %o', files); this.uploadFiles(files); }; handleFiles(e) { DEBUG('Selected files %o', e.target.files); this.uploadFiles(e.target.files); event.target.value = ''; } uploadFiles(files) { for (var key in files) { // is the item a File? if (files.hasOwnProperty(key) && files[key] instanceof File) { let uploadRequest; let complete = false; const filename = files[key].name let progressNotification = this.props.notificationCenter().postFileUploadProgress( filename, (notification) => { if (!complete) { uploadRequest.abort(); this.uploads.splice(this.uploads.indexOf(uploadRequest), 1); } } ); uploadRequest = superagent - .post(`${config.fileSharingUrl}/${this.props.remoteIdentity}/${this.props.call.id}/${filename}`) + .post(`${config.fileSharingUrl}/${this.props.remoteUri}/${this.props.call.id}/${filename}`) .send(files[key]) .on('progress', (e) => { this.props.notificationCenter().editFileUploadNotification(e.percent, progressNotification); }) .end((err, response) => { complete = true; this.props.notificationCenter().removeFileUploadNotification(progressNotification); if (err) { this.props.notificationCenter().postFileUploadFailed(filename); } this.uploads.splice(this.uploads.indexOf(uploadRequest), 1); }); this.uploads.push([uploadRequest, progressNotification]); } } } downloadFile(filename) { // const a = document.createElement('a'); - // a.href = `${config.fileSharingUrl}/${this.props.remoteIdentity}/${this.props.call.id}/${filename}`; + // a.href = `${config.fileSharingUrl}/${this.props.remoteUri}/${this.props.call.id}/${filename}`; // a.target = '_blank'; // a.download = filename; // const clickEvent = document.createEvent('MouseEvent'); // clickEvent.initMouseEvent('click', true, true, window, 0, // clickEvent.screenX, clickEvent.screenY, clickEvent.clientX, clickEvent.clientY, // clickEvent.ctrlKey, clickEvent.altKey, clickEvent.shiftKey, clickEvent.metaKey, // 0, null); // a.dispatchEvent(clickEvent); } preventOverlay(event) { // Stop the overlay when we are the thumbnail bar event.stopPropagation(); } muteAudio(event) { event.preventDefault(); const localStream = this.props.call.getLocalStreams()[0]; if (localStream.getAudioTracks().length > 0) { const track = localStream.getAudioTracks()[0]; if(this.state.audioMuted) { DEBUG('Unmute microphone'); track.enabled = true; this.setState({audioMuted: false}); } else { DEBUG('Mute microphone'); track.enabled = false; this.setState({audioMuted: true}); } } } toggleCamera(event) { event.preventDefault(); const localStream = this.props.call.getLocalStreams()[0]; if (localStream.getVideoTracks().length > 0) { const track = localStream.getVideoTracks()[0]; track._switchCamera(); } } muteVideo(event) { event.preventDefault(); const localStream = this.props.call.getLocalStreams()[0]; if (localStream.getVideoTracks().length > 0) { const track = localStream.getVideoTracks()[0]; if (this.state.videoMuted) { DEBUG('Unmute camera'); track.enabled = true; this.setState({videoMuted: false}); } else { DEBUG('Mute camera'); track.enabled = false; this.setState({videoMuted: true}); } } } hangup(event) { event.preventDefault(); for (let participant of this.state.participants) { participant.detach(); } this.props.hangup(); } armOverlayTimer() { clearTimeout(this.overlayTimer); this.overlayTimer = setTimeout(() => { this.setState({callOverlayVisible: false}); }, 4000); } showOverlay() { // if (!this.state.shareOverlayVisible && !this.state.showDrawer && !this.state.showFiles) { // if (!this.state.callOverlayVisible) { // this.setState({callOverlayVisible: true}); // } // this.armOverlayTimer(); // } } toggleInviteModal() { this.setState({showInviteModal: !this.state.showInviteModal}); } toggleDrawer() { this.setState({callOverlayVisible: true, showDrawer: !this.state.showDrawer, showFiles: false}); clearTimeout(this.overlayTimer); } toggleFiles() { this.setState({callOverlayVisible: true, showFiles: !this.state.showFiles, showDrawer: false}); clearTimeout(this.overlayTimer); } showFiles() { this.setState({callOverlayVisible: true, showFiles: true, showDrawer: false}); clearTimeout(this.overlayTimer); } render() { if (this.props.call === null) { return (); } let watermark; const largeVideoClasses = classNames({ 'animated' : true, 'fadeIn' : true, 'large' : true, 'mirror' : !this.props.call.sharingScreen && !this.props.generatedVideoTrack, 'fit' : this.props.call.sharingScreen }); let matrixClasses = classNames({ 'matrix' : true }); const containerClasses = classNames({ 'video-container': true, 'conference': true, 'drawer-visible': this.state.showDrawer || this.state.showFiles }); - const remoteIdentity = this.props.remoteIdentity.split('@')[0]; + const remoteUri = this.props.remoteUri.split('@')[0]; // const shareOverlay = ( // // // // // Invite other online users of this service, share this link with others or email, so they can easily join this conference. // // // // // // // // // // // // ); const buttons = {}; // const commonButtonTopClasses = classNames({ // 'btn' : true, // 'btn-link' : true // }); // const fullScreenButtonIcons = classNames({ // 'fa' : true, // 'fa-2x' : true, // 'fa-expand' : !this.isFullScreen(), // 'fa-compress' : this.isFullScreen() // }); const topButtons = []; // if (!this.state.showFiles) { // if (this.state.sharedFiles.length !== 0) { // topButtons.push( // // // // ); // } // } if (!this.state.showDrawer) { topButtons.push(); } buttons.top = {right: topButtons}; const muteButtonIcons = this.state.audioMuted ? 'microphone-off' : 'microphone'; const muteVideoButtonIcons = this.state.videoMuted ? 'video-off' : 'video'; const buttonClass = (Platform.OS === 'ios') ? styles.iosButton : styles.androidButton; const bottomButtons = []; bottomButtons.push( ); if (this.haveVideo) { bottomButtons.push( ); } bottomButtons.push( ); if (this.haveVideo) { bottomButtons.push( ); } bottomButtons.push( ) // bottomButtons.push( // // // // ); bottomButtons.push( ); buttons.bottom = bottomButtons; const participants = []; if (this.state.participants.length > 0) { if (this.state.activeSpeakers.findIndex((element) => {return element.id === this.props.call.id}) === -1) { participants.push( ); } } const drawerParticipants = []; drawerParticipants.push( ); let videos = []; if (this.state.participants.length === 0) { videos.push( ); } else { const activeSpeakers = this.state.activeSpeakers; const activeSpeakersCount = activeSpeakers.length; if (activeSpeakersCount > 0) { activeSpeakers.forEach((p) => { videos.push( ); }); this.state.participants.forEach((p) => { if (this.state.activeSpeakers.indexOf(p) === -1) { participants.push( ); } drawerParticipants.push( ); }); } else { this.state.participants.forEach((p) => { videos.push( ); drawerParticipants.push( ); }); } } // let filesDrawerContent = ( // // ); return ( {videos} {participants} {drawerParticipants} ); } } ConferenceBox.propTypes = { notificationCenter : PropTypes.func.isRequired, call : PropTypes.object, hangup : PropTypes.func, - remoteIdentity : PropTypes.string, + remoteUri : PropTypes.string, generatedVideoTrack : PropTypes.bool, toggleSpeakerPhone : PropTypes.func, speakerPhoneEnabled : PropTypes.bool }; export default ConferenceBox; diff --git a/app/components/ConferenceHeader.js b/app/components/ConferenceHeader.js index 5d7bae9..7d592c3 100644 --- a/app/components/ConferenceHeader.js +++ b/app/components/ConferenceHeader.js @@ -1,85 +1,85 @@ import React, { useState, useEffect, useRef, Fragment } from 'react'; import { View } from 'react-native'; import PropTypes from 'prop-types'; import moment from 'moment'; import momentFormat from 'moment-duration-format'; import { Text, Appbar } from 'react-native-paper'; import Icon from 'react-native-vector-icons/MaterialCommunityIcons'; import styles from '../assets/styles/blink/_ConferenceHeader.scss'; const useInterval = (callback, delay) => { const savedCallback = useRef(); // Remember the latest callback. useEffect(() => { savedCallback.current = callback; }, [callback]); // Set up the interval. useEffect(() => { function tick() { savedCallback.current(); } if (delay !== null) { let id = setInterval(tick, delay); return () => clearInterval(id); } }, [delay]); } const ConferenceHeader = (props) => { let [seconds, setSeconds] = useState(0); useInterval(() => { setSeconds(seconds + 1); }, 1000); const duration = moment.duration(seconds, 'seconds').format('hh:mm:ss', {trim: false}); let videoHeader; let callButtons; if (props.show) { const participantCount = props.participants.length + 1; // const callDetail = ( // // {duration} - {participantCount} participant{participantCount > 1 ? 's' : ''} // // ); const callDetail = `${duration} - ${participantCount} participant${participantCount > 1 ? 's' : ''}`; videoHeader = ( {props.buttons.top.right} ); callButtons = ( {props.buttons.bottom} ); } return ( {videoHeader} {callButtons} ); } ConferenceHeader.propTypes = { show: PropTypes.bool.isRequired, - remoteIdentity: PropTypes.string.isRequired, + remoteUri: PropTypes.string.isRequired, participants: PropTypes.array.isRequired, buttons: PropTypes.object.isRequired }; export default ConferenceHeader; diff --git a/app/components/ReadyBox.js b/app/components/ReadyBox.js index 15e0bed..c189f1d 100644 --- a/app/components/ReadyBox.js +++ b/app/components/ReadyBox.js @@ -1,208 +1,208 @@ import React, { Component, Fragment } from 'react'; import PropTypes from 'prop-types'; import classNames from 'classnames'; // import VizSensor = require('react-visibility-sensor').default; import autoBind from 'auto-bind'; import { View, Platform} from 'react-native'; import { IconButton, Title } from 'react-native-paper'; import ConferenceModal from './ConferenceModal'; import HistoryCard from './HistoryCard'; import HistoryTileBox from './HistoryTileBox'; import FooterBox from './FooterBox'; import URIInput from './URIInput'; import config from '../config'; import utils from '../utils'; import styles from '../assets/styles/blink/_ReadyBox.scss'; class ReadyBox extends Component { constructor(props) { super(props); autoBind(this); this.state = { targetUri: this.props.missedTargetUri, showConferenceModal: false, sticky: false, }; } getTargetUri() { const defaultDomain = this.props.account.id.substring(this.props.account.id.indexOf('@') + 1); return utils.normalizeUri(this.state.targetUri, defaultDomain); } handleTargetChange(value) { if (this.state.targetUri) { let currentUri = this.getTargetUri(); if (currentUri.trim() === value.trim()) { this.setState({targetUri: ''}); } else { this.setState({targetUri: value}); } } else { this.setState({targetUri: value}); } this.forceUpdate(); } handleTargetSelect() { if (this.props.connection === null) { this.props._notificationCenter.postSystemNotification("Server unreachable", {timeout: 2}); return; } // the user pressed enter, start a video call by default if (this.state.targetUri.endsWith(`@${config.defaultConferenceDomain}`)) { this.props.startConference(this.state.targetUri, {audio: true, video: true}); } else { this.props.startCall(this.getTargetUri(), {audio: true, video: true}); } } showConferenceModal(event) { event.preventDefault(); if (this.state.targetUri.length !== 0) { const uri = `${this.state.targetUri.split('@')[0].replace(/[\s()-]/g, '')}@${config.defaultConferenceDomain}`; this.handleConferenceCall(uri.toLowerCase()); } else { this.setState({showConferenceModal: true}); } } handleAudioCall(event) { if (this.props.connection === null) { this.props._notificationCenter.postSystemNotification("Server unreachable", {timeout: 2}); return; } event.preventDefault(); if (this.state.targetUri.endsWith(`@${config.defaultConferenceDomain}`)) { this.props.startConference(this.state.targetUri, {audio: true, video: false}); } else { this.props.startCall(this.getTargetUri(), {audio: true, video: false}); } } handleVideoCall(event) { if (this.props.connection === null) { this.props._notificationCenter.postSystemNotification("Server unreachable", {timeout: 2}); return; } event.preventDefault(); if (this.state.targetUri.endsWith(`@${config.defaultConferenceDomain}`)) { this.props.startConference(this.state.targetUri, {audio: true, video: false}); } else { this.props.startCall(this.getTargetUri(), {audio: true, video: true}); } } handleConferenceCall(targetUri, options={audio: true, video: true}) { if (targetUri) { if (!options.video) { - console.log('Handle audio only conference call to',targetUri); + console.log('ReadyBox: Handle audio only conference call to',targetUri); } else { - console.log('Handle video conference call to',targetUri); + console.log('ReadyBox: Handle video conference call to',targetUri); } this.props.startConference(targetUri, options); } this.setState({showConferenceModal: false}); } render() { const defaultDomain = `${config.defaultDomain}`; const buttonClass = (Platform.OS === 'ios') ? styles.iosButton : styles.androidButton; const uriGroupClass = this.props.orientation === 'landscape' ? styles.landscapeUriButtonGroup : styles.portraitUriButtonGroup; const uriClass = this.props.orientation === 'landscape' ? styles.landscapeUriInputBox : styles.portraitUriInputBox; const titleClass = this.props.orientation === 'landscape' ? styles.landscapeTitle : styles.portraitTitle; const historyClass = this.props.orientation === 'landscape' ? styles.landscapeHistory : styles.portraitHistory; // Join URIs from local and server history for input let history = this.props.history.concat( this.props.serverHistory.map(e => e.remoteParty) ); history = [...new Set(history)]; //console.log('history from server is', this.props.serverHistory); const placehoder = 'Enter a SIP address like alice@' + defaultDomain; return ( Enter address or phone number {this.props.serverHistory.filter(historyItem => historyItem.remoteParty.startsWith(this.state.targetUri)).map((historyItem, idx) => () )} ); } } ReadyBox.propTypes = { account : PropTypes.object.isRequired, startCall : PropTypes.func.isRequired, startConference : PropTypes.func.isRequired, missedTargetUri : PropTypes.string, history : PropTypes.array, serverHistory : PropTypes.array, orientation : PropTypes.string }; export default ReadyBox;