initial pass on audio-only calls

This commit is contained in:
or-else
2023-01-04 12:47:02 -08:00
parent 1bc6f87844
commit 80d48077c9
23 changed files with 180 additions and 40 deletions
+8
View File
@@ -1649,6 +1649,10 @@ img.channel {
z-index: 2;
}
.call-party.self[disabled] {
display: none;
}
video {
width: 100%;
height: 100%;
@@ -1659,6 +1663,10 @@ video {
transform: scaleX(-1);
}
.call-party[disabled] video {
display: none;
}
.caller-card {
position: absolute;
top: 0;
+1 -1
View File
File diff suppressed because one or more lines are too long
+1
View File
@@ -7,6 +7,7 @@
"url": "https://github.com/tinode/example-react-js.git"
},
"scripts": {
"start": "echo 'Read installation instructions'",
"build": "npm run vers && npm run build:css && npm run extract && npm run build:i18n && npm run build:prod && npm run build:dev",
"clean": "rm umd/* && rm css/base.min.css && rm i18n.min/*",
"build:css": "postcss css/base.css > css/base.min.css",
+1 -1
View File
File diff suppressed because one or more lines are too long
+7
View File
@@ -1542,5 +1542,12 @@
"description": "Label for call in progress",
"missing": false,
"obsolete": false
},
"menu_item_audio_call": {
"translation": "",
"defaultMessage": "Call",
"description": "Start audio call",
"missing": true,
"obsolete": false
}
}
+7
View File
@@ -1542,5 +1542,12 @@
"description": "Label for call in progress",
"missing": false,
"obsolete": false
},
"menu_item_audio_call": {
"translation": "Call",
"defaultMessage": "Call",
"description": "Start audio call",
"missing": false,
"obsolete": false
}
}
+7
View File
@@ -1542,5 +1542,12 @@
"description": "Label for call in progress",
"missing": false,
"obsolete": false
},
"menu_item_audio_call": {
"translation": "",
"defaultMessage": "Call",
"description": "Start audio call",
"missing": true,
"obsolete": false
}
}
+4
View File
@@ -500,6 +500,10 @@
"defaultMessage": "Archive",
"description": "Move topic from the list of active chats to archive"
},
"menu_item_audio_call": {
"defaultMessage": "Call",
"description": "Start audio call"
},
"menu_item_block": {
"defaultMessage": "Block",
"description": "Block topic or user"
+7
View File
@@ -1542,5 +1542,12 @@
"description": "Label for call in progress",
"missing": false,
"obsolete": false
},
"menu_item_audio_call": {
"translation": "",
"defaultMessage": "Call",
"description": "Start audio call",
"missing": true,
"obsolete": false
}
}
+7
View File
@@ -1542,5 +1542,12 @@
"description": "Label for call in progress",
"missing": false,
"obsolete": false
},
"menu_item_audio_call": {
"translation": "",
"defaultMessage": "Call",
"description": "Start audio call",
"missing": true,
"obsolete": false
}
}
+7
View File
@@ -1542,5 +1542,12 @@
"description": "Label for call in progress",
"missing": false,
"obsolete": false
},
"menu_item_audio_call": {
"translation": "",
"defaultMessage": "Call",
"description": "Start audio call",
"missing": true,
"obsolete": false
}
}
+7
View File
@@ -1542,5 +1542,12 @@
"description": "Label for call in progress",
"missing": false,
"obsolete": false
},
"menu_item_audio_call": {
"translation": "",
"defaultMessage": "Call",
"description": "Start audio call",
"missing": true,
"obsolete": false
}
}
+7
View File
@@ -1542,5 +1542,12 @@
"description": "Label for call in progress",
"missing": false,
"obsolete": false
},
"menu_item_audio_call": {
"translation": "",
"defaultMessage": "Call",
"description": "Start audio call",
"missing": true,
"obsolete": false
}
}
+7
View File
@@ -1542,5 +1542,12 @@
"description": "Label for call in progress",
"missing": false,
"obsolete": false
},
"menu_item_audio_call": {
"translation": "",
"defaultMessage": "Call",
"description": "Start audio call",
"missing": true,
"obsolete": false
}
}
+1
View File
@@ -1348,6 +1348,7 @@ class MessagesView extends React.Component {
topic={this.state.topic}
seq={this.props.callSeq}
callState={this.props.callState}
callAudioOnly={this.props.callAudioOnly}
tinode={this.props.tinode}
title={this.state.title}
avatar={this.state.avatar || true}
+31 -6
View File
@@ -69,6 +69,11 @@ const messages = defineMessages({
defaultMessage: 'Info',
description: 'Show extended topic information'
},
menu_item_audio_call: {
id: 'menu_item_audio_call',
defaultMessage: 'Call',
description: 'Start audio call'
},
menu_item_video_call: {
id: 'menu_item_video_call',
defaultMessage: 'Video call',
@@ -158,6 +163,7 @@ class TinodeWeb extends React.Component {
this.handleHideForwardDialog = this.handleHideForwardDialog.bind(this);
this.handleStartVideoCall = this.handleStartVideoCall.bind(this);
this.handleStartAudioCall = this.handleStartAudioCall.bind(this);
this.handleInfoMessage = this.handleInfoMessage.bind(this);
this.handleDataMessage = this.handleDataMessage.bind(this);
this.handleCallClose = this.handleCallClose.bind(this);
@@ -233,6 +239,7 @@ class TinodeWeb extends React.Component {
// Video calls.
callTopic: undefined,
callState: CALL_STATE_NONE,
callAudioOnly: undefined,
// If true, call state should be transitioned to CALL_STATE_IN_PROGRESS upon
// switching to the call topic.
callShouldStart: false,
@@ -1584,6 +1591,10 @@ class TinodeWeb extends React.Component {
title: this.props.intl.formatMessage(messages.menu_item_info),
handler: this.handleShowInfoView
} : null,
subscribed && Tinode.isP2PTopicName(topicName) && webrtc ? {
title: this.props.intl.formatMessage(messages.menu_item_audio_call),
handler: this.handleStartAudioCall
} : null,
subscribed && Tinode.isP2PTopicName(topicName) && webrtc ? {
title: this.props.intl.formatMessage(messages.menu_item_video_call),
handler: this.handleStartVideoCall
@@ -1709,14 +1720,23 @@ class TinodeWeb extends React.Component {
handleStartVideoCall() {
this.setState({
callTopic: this.state.topicSelected,
callState: CALL_STATE_OUTGOING_INITATED
callState: CALL_STATE_OUTGOING_INITATED,
callAudioOnly: false
});
}
handleCallInvite(callTopic, callSeq, callState) {
handleStartAudioCall() {
this.setState({
callTopic: this.state.topicSelected,
callState: CALL_STATE_OUTGOING_INITATED,
callAudioOnly: true
});
}
handleCallInvite(callTopic, callSeq, callState, audioOnly) {
switch (callState) {
case CALL_STATE_OUTGOING_INITATED:
let head = { webrtc: CALL_HEAD_STARTED };
const head = { webrtc: CALL_HEAD_STARTED, aonly: !!audioOnly };
this.handleSendMessage(Drafty.videoCall(), undefined, undefined, head)
.then(ctrl => {
if (ctrl.code < 200 || ctrl.code >= 300 || !ctrl.params || !ctrl.params.seq) {
@@ -1787,7 +1807,8 @@ class TinodeWeb extends React.Component {
}
this.setState({
callTopic: undefined,
callState: CALL_STATE_NONE
callState: CALL_STATE_NONE,
callAudioOnly: undefined
});
}
@@ -1820,7 +1841,8 @@ class TinodeWeb extends React.Component {
this.setState({
callTopic: null,
callState: CALL_STATE_NONE,
callSeq: null
callSeq: null,
callAudioOnly: undefined
});
return;
}
@@ -1853,7 +1875,8 @@ class TinodeWeb extends React.Component {
this.setState({
callTopic: data.topic,
callState: CALL_STATE_INCOMING_RECEIVED,
callSeq: data.seq
callSeq: data.seq,
callAudioOnly: !!msg.head.aonly
});
} else {
// Another call is either in progress or being established.
@@ -1913,6 +1936,7 @@ class TinodeWeb extends React.Component {
topic={this.state.callTopic}
seq={this.state.callSeq}
callState={this.state.callState}
audioOnly={this.state.callAudioOnly}
onRinging={this.handleCallRinging}
onAcceptCall={this.handleCallAccept}
onReject={this.handleCallHangup}
@@ -2024,6 +2048,7 @@ class TinodeWeb extends React.Component {
callTopic={this.state.callTopic}
callSeq={this.state.callSeq}
callState={this.state.callState}
callAudioOnly={this.state.callAudioOnly}
onCallHangup={this.handleCallHangup}
onCallInvite={this.handleCallInvite}
+15 -10
View File
@@ -37,9 +37,11 @@ class CallPanel extends React.PureComponent {
callInitialSetupComplete: false
};
console.log("CallPanel", props.callAudioOnly);
this.localStreamConstraints = {
audio: true,
video: true
video: !props.callAudioOnly
};
this.isOutgoingCall = props.callState == CALL_STATE_OUTGOING_INITATED;
@@ -146,7 +148,7 @@ class CallPanel extends React.PureComponent {
if (this.props.callState == CALL_STATE_IN_PROGRESS) {
// We apparently just accepted the call.
this.props.onInvite(this.props.topic, this.props.seq, this.props.callState);
this.props.onInvite(this.props.topic, this.props.seq, this.props.callState, this.props.callAudioOnly);
return;
}
@@ -160,7 +162,7 @@ class CallPanel extends React.PureComponent {
DIALING_SOUND.play();
// Send call invitation.
this.props.onInvite(this.props.topic, this.props.seq, this.props.callState);
this.props.onInvite(this.props.topic, this.props.seq, this.props.callState, this.props.callAudioOnly);
})
.catch(this.handleGetUserMediaError);
}
@@ -410,9 +412,12 @@ class CallPanel extends React.PureComponent {
render() {
const remoteActive = this.remoteRef.current && this.remoteRef.current.srcObject;
const disabled = !(this.state.localStream && this.state.localStream.getTracks());
const audioIcon = this.state.localStream && this.state.localStream.getAudioTracks()[0].enabled ? 'mic' : 'mic_off';
const videoIcon = this.state.localStream && this.state.localStream.getVideoTracks()[0].enabled ? 'videocam' : 'videocam_off';
const noVideo = this.props.callAudioOnly;
const audioTracks = this.state.localStream && this.state.localStream.getAudioTracks();
const videoTracks = !noVideo && this.state.localStream && this.state.localStream.getVideoTracks();
const enabled = !!(audioTracks && audioTracks[0]);
const audioIcon = audioTracks && audioTracks[0] && audioTracks[0].enabled ? 'mic' : 'mic_off';
const videoIcon = videoTracks && videoTracks[0] && videoTracks[0].enabled ? 'videocam' : 'videocam_off';
const peerTitle = clipStr(this.props.title, MAX_PEER_TITLE_LENGTH);
const pulseAnimation = this.state.waitingForPeer ? ' pulse' : '';
@@ -420,14 +425,14 @@ class CallPanel extends React.PureComponent {
<>
<div id="video-container">
<div id="video-container-panel">
<div className="call-party self">
<div className="call-party self" disabled={noVideo}>
<video ref={this.localRef} autoPlay muted playsInline />
<div className="caller-name inactive">
<FormattedMessage id="calls_you_label"
defaultMessage="You" description="Shown over the local video screen" />
</div>
</div>
<div className="call-party peer">
<div className="call-party peer" disabled={noVideo}>
<video ref={this.remoteRef} autoPlay playsInline />
{remoteActive ?
<div className="caller-name inactive">{peerTitle}</div> :
@@ -448,10 +453,10 @@ class CallPanel extends React.PureComponent {
<button className="danger" onClick={this.handleCloseClick}>
<i className="material-icons">call_end</i>
</button>
<button className="secondary" onClick={this.handleToggleCameraClick} disabled={disabled}>
<button className="secondary" onClick={this.handleToggleCameraClick} disabled={!enabled}>
<i className="material-icons">{videoIcon}</i>
</button>
<button className="secondary" onClick={this.handleToggleMicClick} disabled={disabled}>
<button className="secondary" onClick={this.handleToggleMicClick} disabled={!enabled}>
<i className="material-icons">{audioIcon}</i>
</button>
</div>
+1 -1
View File
File diff suppressed because one or more lines are too long
+50 -17
View File
@@ -6629,6 +6629,7 @@ class MessagesView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compone
topic: this.state.topic,
seq: this.props.callSeq,
callState: this.props.callState,
callAudioOnly: this.props.callAudioOnly,
tinode: this.props.tinode,
title: this.state.title,
avatar: this.state.avatar || true,
@@ -7766,6 +7767,13 @@ const messages = (0,react_intl__WEBPACK_IMPORTED_MODULE_1__.defineMessages)({
"value": "Info"
}]
},
menu_item_audio_call: {
id: "menu_item_audio_call",
defaultMessage: [{
"type": 0,
"value": "Call"
}]
},
menu_item_video_call: {
id: "menu_item_video_call",
defaultMessage: [{
@@ -7853,6 +7861,7 @@ class TinodeWeb extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component)
this.handleShowForwardDialog = this.handleShowForwardDialog.bind(this);
this.handleHideForwardDialog = this.handleHideForwardDialog.bind(this);
this.handleStartVideoCall = this.handleStartVideoCall.bind(this);
this.handleStartAudioCall = this.handleStartAudioCall.bind(this);
this.handleInfoMessage = this.handleInfoMessage.bind(this);
this.handleDataMessage = this.handleDataMessage.bind(this);
this.handleCallClose = this.handleCallClose.bind(this);
@@ -7907,6 +7916,7 @@ class TinodeWeb extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component)
mobilePanel: 'sidepanel',
callTopic: undefined,
callState: _constants_js__WEBPACK_IMPORTED_MODULE_13__.CALL_STATE_NONE,
callAudioOnly: undefined,
callShouldStart: false,
contextMenuVisible: false,
contextMenuBounds: null,
@@ -9099,6 +9109,9 @@ class TinodeWeb extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component)
return [subscribed ? {
title: this.props.intl.formatMessage(messages.menu_item_info),
handler: this.handleShowInfoView
} : null, subscribed && tinode_sdk__WEBPACK_IMPORTED_MODULE_4__.Tinode.isP2PTopicName(topicName) && webrtc ? {
title: this.props.intl.formatMessage(messages.menu_item_audio_call),
handler: this.handleStartAudioCall
} : null, subscribed && tinode_sdk__WEBPACK_IMPORTED_MODULE_4__.Tinode.isP2PTopicName(topicName) && webrtc ? {
title: this.props.intl.formatMessage(messages.menu_item_video_call),
handler: this.handleStartVideoCall
@@ -9207,14 +9220,23 @@ class TinodeWeb extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component)
handleStartVideoCall() {
this.setState({
callTopic: this.state.topicSelected,
callState: _constants_js__WEBPACK_IMPORTED_MODULE_13__.CALL_STATE_OUTGOING_INITATED
callState: _constants_js__WEBPACK_IMPORTED_MODULE_13__.CALL_STATE_OUTGOING_INITATED,
callAudioOnly: false
});
}
handleCallInvite(callTopic, callSeq, callState) {
handleStartAudioCall() {
this.setState({
callTopic: this.state.topicSelected,
callState: _constants_js__WEBPACK_IMPORTED_MODULE_13__.CALL_STATE_OUTGOING_INITATED,
callAudioOnly: true
});
}
handleCallInvite(callTopic, callSeq, callState, audioOnly) {
switch (callState) {
case _constants_js__WEBPACK_IMPORTED_MODULE_13__.CALL_STATE_OUTGOING_INITATED:
let head = {
webrtc: _constants_js__WEBPACK_IMPORTED_MODULE_13__.CALL_HEAD_STARTED
const head = {
webrtc: _constants_js__WEBPACK_IMPORTED_MODULE_13__.CALL_HEAD_STARTED,
aonly: !!audioOnly
};
this.handleSendMessage(tinode_sdk__WEBPACK_IMPORTED_MODULE_4__.Drafty.videoCall(), undefined, undefined, head).then(ctrl => {
if (ctrl.code < 200 || ctrl.code >= 300 || !ctrl.params || !ctrl.params.seq) {
@@ -9276,7 +9298,8 @@ class TinodeWeb extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component)
}
this.setState({
callTopic: undefined,
callState: _constants_js__WEBPACK_IMPORTED_MODULE_13__.CALL_STATE_NONE
callState: _constants_js__WEBPACK_IMPORTED_MODULE_13__.CALL_STATE_NONE,
callAudioOnly: undefined
});
}
handleCallAccept(topicName) {
@@ -9305,7 +9328,8 @@ class TinodeWeb extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component)
this.setState({
callTopic: null,
callState: _constants_js__WEBPACK_IMPORTED_MODULE_13__.CALL_STATE_NONE,
callSeq: null
callSeq: null,
callAudioOnly: undefined
});
return;
}
@@ -9331,7 +9355,8 @@ class TinodeWeb extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component)
this.setState({
callTopic: data.topic,
callState: _constants_js__WEBPACK_IMPORTED_MODULE_13__.CALL_STATE_INCOMING_RECEIVED,
callSeq: data.seq
callSeq: data.seq,
callAudioOnly: !!msg.head.aonly
});
} else {
this.handleCallHangup(data.topic, data.seq);
@@ -9378,6 +9403,7 @@ class TinodeWeb extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component)
topic: this.state.callTopic,
seq: this.state.callSeq,
callState: this.state.callState,
audioOnly: this.state.callAudioOnly,
onRinging: this.handleCallRinging,
onAcceptCall: this.handleCallAccept,
onReject: this.handleCallHangup
@@ -9480,6 +9506,7 @@ class TinodeWeb extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component)
callTopic: this.state.callTopic,
callSeq: this.state.callSeq,
callState: this.state.callState,
callAudioOnly: this.state.callAudioOnly,
onCallHangup: this.handleCallHangup,
onCallInvite: this.handleCallInvite,
onCallSendOffer: this.handleCallSendOffer,
@@ -10675,9 +10702,10 @@ class CallPanel extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureCompon
waitingForPeer: false,
callInitialSetupComplete: false
};
console.log("CallPanel", props.callAudioOnly);
this.localStreamConstraints = {
audio: true,
video: true
video: !props.callAudioOnly
};
this.isOutgoingCall = props.callState == _constants_js__WEBPACK_IMPORTED_MODULE_4__.CALL_STATE_OUTGOING_INITATED;
this.localRef = react__WEBPACK_IMPORTED_MODULE_0___default().createRef();
@@ -10765,7 +10793,7 @@ class CallPanel extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureCompon
return;
}
if (this.props.callState == _constants_js__WEBPACK_IMPORTED_MODULE_4__.CALL_STATE_IN_PROGRESS) {
this.props.onInvite(this.props.topic, this.props.seq, this.props.callState);
this.props.onInvite(this.props.topic, this.props.seq, this.props.callState, this.props.callAudioOnly);
return;
}
navigator.mediaDevices.getUserMedia(this.localStreamConstraints).then(stream => {
@@ -10775,7 +10803,7 @@ class CallPanel extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureCompon
});
this.localRef.current.srcObject = stream;
DIALING_SOUND.play();
this.props.onInvite(this.props.topic, this.props.seq, this.props.callState);
this.props.onInvite(this.props.topic, this.props.seq, this.props.callState, this.props.callAudioOnly);
}).catch(this.handleGetUserMediaError);
}
stop() {
@@ -10981,9 +11009,12 @@ class CallPanel extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureCompon
}
render() {
const remoteActive = this.remoteRef.current && this.remoteRef.current.srcObject;
const disabled = !(this.state.localStream && this.state.localStream.getTracks());
const audioIcon = this.state.localStream && this.state.localStream.getAudioTracks()[0].enabled ? 'mic' : 'mic_off';
const videoIcon = this.state.localStream && this.state.localStream.getVideoTracks()[0].enabled ? 'videocam' : 'videocam_off';
const noVideo = this.props.callAudioOnly;
const audioTracks = this.state.localStream && this.state.localStream.getAudioTracks();
const videoTracks = !noVideo && this.state.localStream && this.state.localStream.getVideoTracks();
const enabled = !!(audioTracks && audioTracks[0]);
const audioIcon = audioTracks && audioTracks[0] && audioTracks[0].enabled ? 'mic' : 'mic_off';
const videoIcon = videoTracks && videoTracks[0] && videoTracks[0].enabled ? 'videocam' : 'videocam_off';
const peerTitle = (0,_lib_utils_js__WEBPACK_IMPORTED_MODULE_5__.clipStr)(this.props.title, _config_js__WEBPACK_IMPORTED_MODULE_3__.MAX_PEER_TITLE_LENGTH);
const pulseAnimation = this.state.waitingForPeer ? ' pulse' : '';
return react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
@@ -10991,7 +11022,8 @@ class CallPanel extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureCompon
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
id: "video-container-panel"
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: "call-party self"
className: "call-party self",
disabled: noVideo
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("video", {
ref: this.localRef,
autoPlay: true,
@@ -11006,7 +11038,8 @@ class CallPanel extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureCompon
"value": "You"
}]
}))), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: "call-party peer"
className: "call-party peer",
disabled: noVideo
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("video", {
ref: this.remoteRef,
autoPlay: true,
@@ -11034,13 +11067,13 @@ class CallPanel extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureCompon
}, "call_end")), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", {
className: "secondary",
onClick: this.handleToggleCameraClick,
disabled: disabled
disabled: !enabled
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("i", {
className: "material-icons"
}, videoIcon)), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", {
className: "secondary",
onClick: this.handleToggleMicClick,
disabled: disabled
disabled: !enabled
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("i", {
className: "material-icons"
}, audioIcon)))));
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long