outgoing call panel looks good

This commit is contained in:
or-else
2022-05-27 10:40:38 -07:00
parent 4e7c3419c0
commit 92aa11be39
10 changed files with 106 additions and 56 deletions
+44 -9
View File
@@ -1608,18 +1608,18 @@ img.channel {
width: 100%; width: 100%;
} }
.video-elem { .call-party {
background-color: #eee; background-color: #eee;
border-radius: 0.5rem; border-radius: 0.5rem;
border: 2px solid #ccc; border: 2px solid #ccc;
overflow: hidden; overflow: hidden;
} }
.video-elem:hover { .call-party:hover {
border: 2px solid #2196f3; border: 2px solid #2196f3;
} }
.video-elem.peer { .call-party.peer {
position: absolute; position: absolute;
top: 1rem; top: 1rem;
left: 1rem; left: 1rem;
@@ -1628,7 +1628,7 @@ img.channel {
z-index: 1; z-index: 1;
} }
.video-elem.self { .call-party.self {
position: absolute; position: absolute;
right: 0.5rem; right: 0.5rem;
bottom: 0.5rem; bottom: 0.5rem;
@@ -1643,15 +1643,30 @@ video {
object-fit: contain; object-fit: contain;
} }
.video-elem.self video { .call-party.self video {
transform: scaleX(-1); transform: scaleX(-1);
} }
/* "You" badge. */ .peer-card {
.video-title {
position: absolute; position: absolute;
bottom: 0.25rem; top: 0;
right: 0.25rem; left: 0;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
}
.peer-card .avatar-box {
width: 10rem;
height: 10rem;
font-size: 6rem;
}
/* Badge with "You" for current user or peer's name. */
.call-party .title {
padding: 0.15rem 0.5rem; padding: 0.15rem 0.5rem;
border-radius: 0.35rem; border-radius: 0.35rem;
z-index: 1; z-index: 1;
@@ -1659,6 +1674,26 @@ video {
background-color: rgba(255, 255, 255, 0.7); background-color: rgba(255, 255, 255, 0.7);
} }
.call-party.self .title.inactive {
position: absolute;
bottom: 0.25rem;
right: 0.25rem;
}
.call-party .peer-card .title {
font-size: larger;
margin-top: 0.5rem;
}
.call-party.peer .title.inactive {
position: absolute;
font-size: larger;
left: 50%;
bottom: 0.5rem;
transform: translateX(-50%);
margin: 0 auto;
}
.video-call-accept { .video-call-accept {
border-radius: 10px; border-radius: 10px;
background-color: green; background-color: green;
+1 -1
View File
File diff suppressed because one or more lines are too long
+2
View File
@@ -89,6 +89,8 @@ export const MAX_ONLINE_IN_TOPIC = 4;
export const MAX_TITLE_LENGTH = 60; export const MAX_TITLE_LENGTH = 60;
// Maximum length of topic description. // Maximum length of topic description.
export const MAX_TOPIC_DESCRIPTION_LENGTH = 360; export const MAX_TOPIC_DESCRIPTION_LENGTH = 360;
// Maximum length of user name when displaying a video call.
export const MAX_PEER_TITLE_LENGTH = 20;
// Length of message previews in chat list, in characters. // Length of message previews in chat list, in characters.
export const MESSAGE_PREVIEW_LENGTH = 80; export const MESSAGE_PREVIEW_LENGTH = 80;
+1 -3
View File
@@ -1046,13 +1046,11 @@ class MessagesView extends React.Component {
onSendMessage={this.sendFileAttachment} /> onSendMessage={this.sendFileAttachment} />
); );
} else if (this.state.rtcPanel) { } else if (this.state.rtcPanel) {
const topic = this.props.tinode.getTopic(this.state.topic);
component2 = ( component2 = (
<CallPanel <CallPanel
topic={topic} topic={this.state.topic}
seq={this.props.callSeq} seq={this.props.callSeq}
callState={this.props.callState} callState={this.props.callState}
displayMobile={this.props.displayMobile}
tinode={this.props.tinode} tinode={this.props.tinode}
title={this.state.title} title={this.state.title}
avatar={this.state.avatar || true} avatar={this.state.avatar || true}
-1
View File
@@ -2019,7 +2019,6 @@ class TinodeWeb extends React.Component {
: :
null null
} }
{this.state.callTopic && this.state.callState == CALL_STATE_INCOMING_RECEIVED ? {this.state.callTopic && this.state.callState == CALL_STATE_INCOMING_RECEIVED ?
<IncomingCallView <IncomingCallView
tinode={this.tinode} tinode={this.tinode}
+26 -19
View File
@@ -4,7 +4,7 @@ import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
import LetterTile from '../widgets/letter-tile.jsx'; import LetterTile from '../widgets/letter-tile.jsx';
import { CALL_WEBRTC_CONFIG, MAX_TITLE_LENGTH } from '../config.js'; import { CALL_WEBRTC_CONFIG, MAX_PEER_TITLE_LENGTH } from '../config.js';
import { CALL_STATE_OUTGOING_INITATED, CALL_STATE_IN_PROGRESS } from '../constants.js'; import { CALL_STATE_OUTGOING_INITATED, CALL_STATE_IN_PROGRESS } from '../constants.js';
import { clipStr } from '../lib/utils.js' import { clipStr } from '../lib/utils.js'
@@ -69,7 +69,7 @@ class CallPanel extends React.PureComponent {
} }
componentDidMount() { componentDidMount() {
const topic = this.props.topic; const topic = this.props.tinode.getTopic(this.props.topic);
this.previousOnInfo = topic.onInfo; this.previousOnInfo = topic.onInfo;
topic.onInfo = this.onInfo; topic.onInfo = this.onInfo;
if ((this.props.callState == CALL_STATE_OUTGOING_INITATED || if ((this.props.callState == CALL_STATE_OUTGOING_INITATED ||
@@ -79,7 +79,8 @@ class CallPanel extends React.PureComponent {
} }
componentWillUnmount() { componentWillUnmount() {
this.props.topic.onInfo = this.previousOnInfo; const topic = this.props.tinode.getTopic(this.props.topic);
topic.onInfo = this.previousOnInfo;
this.stop(); this.stop();
} }
@@ -122,7 +123,7 @@ class CallPanel extends React.PureComponent {
if (this.props.callState == CALL_STATE_IN_PROGRESS) { if (this.props.callState == CALL_STATE_IN_PROGRESS) {
// We apparently just accepted the call. // We apparently just accepted the call.
this.props.onInvite(this.props.topic.name, this.props.seq, this.props.callState); this.props.onInvite(this.props.topic, this.props.seq, this.props.callState);
return; return;
} }
@@ -133,7 +134,7 @@ class CallPanel extends React.PureComponent {
this.setState({localStream: stream}); this.setState({localStream: stream});
this.localRef.current.srcObject = stream; this.localRef.current.srcObject = stream;
// Send call invitation. // Send call invitation.
this.props.onInvite(this.props.topic.name, this.props.seq, this.props.callState); this.props.onInvite(this.props.topic, this.props.seq, this.props.callState);
}) })
.catch(this.handleGetUserMediaError); .catch(this.handleGetUserMediaError);
} }
@@ -206,14 +207,14 @@ class CallPanel extends React.PureComponent {
return this.state.pc.setLocalDescription(offer); return this.state.pc.setLocalDescription(offer);
}) })
.then(_ => { .then(_ => {
this.props.onSendOffer(this.props.topic.name, this.props.seq, this.state.pc.localDescription.toJSON()); this.props.onSendOffer(this.props.topic, this.props.seq, this.state.pc.localDescription.toJSON());
}) })
.catch(this.reportError); .catch(this.reportError);
} }
handleICECandidateEvent(event) { handleICECandidateEvent(event) {
if (event.candidate) { if (event.candidate) {
this.props.onIceCandidate(this.props.topic.name, this.props.seq, event.candidate.toJSON()); this.props.onIceCandidate(this.props.topic, this.props.seq, event.candidate.toJSON());
} }
} }
@@ -296,7 +297,7 @@ class CallPanel extends React.PureComponent {
return pc.setLocalDescription(answer); return pc.setLocalDescription(answer);
}) })
.then(() => { .then(() => {
this.props.onSendAnswer(this.props.topic.name, this.props.seq, pc.localDescription.toJSON()); this.props.onSendAnswer(this.props.topic, this.props.seq, pc.localDescription.toJSON());
}) })
.catch(this.handleGetUserMediaError); .catch(this.handleGetUserMediaError);
} }
@@ -307,7 +308,7 @@ class CallPanel extends React.PureComponent {
handleCloseClick() { handleCloseClick() {
this.stop(); this.stop();
this.props.onHangup(this.props.topic.name, this.props.seq); this.props.onHangup(this.props.topic, this.props.seq);
} }
// Mute/unmute audio/video (specified by kind). // Mute/unmute audio/video (specified by kind).
@@ -332,31 +333,37 @@ class CallPanel extends React.PureComponent {
} }
render() { render() {
if (!this.props.topic) {
return null;
}
const remoteActive = this.remoteRef.current && this.remoteRef.current.srcObject; const remoteActive = this.remoteRef.current && this.remoteRef.current.srcObject;
const audioIcon = this.state.localStream && this.state.localStream.getAudioTracks()[0].enabled ? 'mic' : 'mic_off'; 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 videoIcon = this.state.localStream && this.state.localStream.getVideoTracks()[0].enabled ? 'videocam' : 'videocam_off';
const peerTitle = clipStr(this.props.title, MAX_PEER_TITLE_LENGTH);
return ( return (
<> <>
<div id="video-container"> <div id="video-container">
<div id="video-container-panel"> <div id="video-container-panel">
<div className="video-elem self"> <div className="call-party self">
<video ref={this.localRef} autoPlay muted playsInline></video> <video ref={this.localRef} autoPlay muted playsInline></video>
<div className="video-title"> <div className="title inactive">
<FormattedMessage id="calls_you_label" <FormattedMessage id="calls_you_label"
defaultMessage="You" description="Shown over the local video screen" /> defaultMessage="You" description="Shown over the local video screen" />
</div> </div>
</div> </div>
<div className="video-elem peer"> <div className="call-party peer">
<video ref={this.remoteRef} autoPlay playsInline></video> <video ref={this.remoteRef} autoPlay playsInline></video>
{remoteActive ? {remoteActive ?
<div className="video-title">{clipStr(this.props.title, MAX_TITLE_LENGTH)}</div> <div className="title inactive">{peerTitle}</div> :
: <div className="peer-card">
null} <div className="avatar-box">
<LetterTile
tinode={this.props.tinode}
avatar={this.props.avatar}
topic={this.props.topic}
title={this.props.title} />
</div>
<div className="title">{peerTitle}</div>
</div>
}
</div> </div>
</div> </div>
<div id="video-container-controls"> <div id="video-container-controls">
+29 -20
View File
@@ -2004,6 +2004,7 @@ __webpack_require__.r(__webpack_exports__);
/* harmony export */ "MAX_IMAGE_DIM": () => (/* binding */ MAX_IMAGE_DIM), /* harmony export */ "MAX_IMAGE_DIM": () => (/* binding */ MAX_IMAGE_DIM),
/* harmony export */ "MAX_INBAND_ATTACHMENT_SIZE": () => (/* binding */ MAX_INBAND_ATTACHMENT_SIZE), /* harmony export */ "MAX_INBAND_ATTACHMENT_SIZE": () => (/* binding */ MAX_INBAND_ATTACHMENT_SIZE),
/* harmony export */ "MAX_ONLINE_IN_TOPIC": () => (/* binding */ MAX_ONLINE_IN_TOPIC), /* harmony export */ "MAX_ONLINE_IN_TOPIC": () => (/* binding */ MAX_ONLINE_IN_TOPIC),
/* harmony export */ "MAX_PEER_TITLE_LENGTH": () => (/* binding */ MAX_PEER_TITLE_LENGTH),
/* harmony export */ "MAX_TAG_COUNT": () => (/* binding */ MAX_TAG_COUNT), /* harmony export */ "MAX_TAG_COUNT": () => (/* binding */ MAX_TAG_COUNT),
/* harmony export */ "MAX_TAG_LENGTH": () => (/* binding */ MAX_TAG_LENGTH), /* harmony export */ "MAX_TAG_LENGTH": () => (/* binding */ MAX_TAG_LENGTH),
/* harmony export */ "MAX_TITLE_LENGTH": () => (/* binding */ MAX_TITLE_LENGTH), /* harmony export */ "MAX_TITLE_LENGTH": () => (/* binding */ MAX_TITLE_LENGTH),
@@ -2054,6 +2055,7 @@ const IMAGE_THUMBNAIL_DIM = 36;
const MAX_ONLINE_IN_TOPIC = 4; const MAX_ONLINE_IN_TOPIC = 4;
const MAX_TITLE_LENGTH = 60; const MAX_TITLE_LENGTH = 60;
const MAX_TOPIC_DESCRIPTION_LENGTH = 360; const MAX_TOPIC_DESCRIPTION_LENGTH = 360;
const MAX_PEER_TITLE_LENGTH = 20;
const MESSAGE_PREVIEW_LENGTH = 80; const MESSAGE_PREVIEW_LENGTH = 80;
const QUOTED_REPLY_LENGTH = 30; const QUOTED_REPLY_LENGTH = 30;
const FORWARDED_PREVIEW_LENGTH = 84; const FORWARDED_PREVIEW_LENGTH = 84;
@@ -6661,12 +6663,10 @@ class MessagesView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compone
onSendMessage: this.sendFileAttachment onSendMessage: this.sendFileAttachment
}); });
} else if (this.state.rtcPanel) { } else if (this.state.rtcPanel) {
const topic = this.props.tinode.getTopic(this.state.topic);
component2 = react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_widgets_call_panel_jsx__WEBPACK_IMPORTED_MODULE_3__["default"], { component2 = react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_widgets_call_panel_jsx__WEBPACK_IMPORTED_MODULE_3__["default"], {
topic: topic, topic: this.state.topic,
seq: this.props.callSeq, seq: this.props.callSeq,
callState: this.props.callState, callState: this.props.callState,
displayMobile: this.props.displayMobile,
tinode: this.props.tinode, tinode: this.props.tinode,
title: this.state.title, title: this.state.title,
avatar: this.state.avatar || true, avatar: this.state.avatar || true,
@@ -11181,7 +11181,7 @@ class CallPanel extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureCompon
} }
componentDidMount() { componentDidMount() {
const topic = this.props.topic; const topic = this.props.tinode.getTopic(this.props.topic);
this.previousOnInfo = topic.onInfo; this.previousOnInfo = topic.onInfo;
topic.onInfo = this.onInfo; topic.onInfo = this.onInfo;
@@ -11191,7 +11191,8 @@ class CallPanel extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureCompon
} }
componentWillUnmount() { componentWillUnmount() {
this.props.topic.onInfo = this.previousOnInfo; const topic = this.props.tinode.getTopic(this.props.topic);
topic.onInfo = this.previousOnInfo;
this.stop(); this.stop();
} }
@@ -11238,7 +11239,7 @@ class CallPanel extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureCompon
} }
if (this.props.callState == _constants_js__WEBPACK_IMPORTED_MODULE_4__.CALL_STATE_IN_PROGRESS) { if (this.props.callState == _constants_js__WEBPACK_IMPORTED_MODULE_4__.CALL_STATE_IN_PROGRESS) {
this.props.onInvite(this.props.topic.name, this.props.seq, this.props.callState); this.props.onInvite(this.props.topic, this.props.seq, this.props.callState);
return; return;
} }
@@ -11247,7 +11248,7 @@ class CallPanel extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureCompon
localStream: stream localStream: stream
}); });
this.localRef.current.srcObject = stream; this.localRef.current.srcObject = stream;
this.props.onInvite(this.props.topic.name, this.props.seq, this.props.callState); this.props.onInvite(this.props.topic, this.props.seq, this.props.callState);
}).catch(this.handleGetUserMediaError); }).catch(this.handleGetUserMediaError);
} }
@@ -11323,13 +11324,13 @@ class CallPanel extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureCompon
this.state.pc.createOffer().then(offer => { this.state.pc.createOffer().then(offer => {
return this.state.pc.setLocalDescription(offer); return this.state.pc.setLocalDescription(offer);
}).then(_ => { }).then(_ => {
this.props.onSendOffer(this.props.topic.name, this.props.seq, this.state.pc.localDescription.toJSON()); this.props.onSendOffer(this.props.topic, this.props.seq, this.state.pc.localDescription.toJSON());
}).catch(this.reportError); }).catch(this.reportError);
} }
handleICECandidateEvent(event) { handleICECandidateEvent(event) {
if (event.candidate) { if (event.candidate) {
this.props.onIceCandidate(this.props.topic.name, this.props.seq, event.candidate.toJSON()); this.props.onIceCandidate(this.props.topic, this.props.seq, event.candidate.toJSON());
} }
} }
@@ -11400,7 +11401,7 @@ class CallPanel extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureCompon
}).then(answer => { }).then(answer => {
return pc.setLocalDescription(answer); return pc.setLocalDescription(answer);
}).then(() => { }).then(() => {
this.props.onSendAnswer(this.props.topic.name, this.props.seq, pc.localDescription.toJSON()); this.props.onSendAnswer(this.props.topic, this.props.seq, pc.localDescription.toJSON());
}).catch(this.handleGetUserMediaError); }).catch(this.handleGetUserMediaError);
} }
@@ -11410,7 +11411,7 @@ class CallPanel extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureCompon
handleCloseClick() { handleCloseClick() {
this.stop(); this.stop();
this.props.onHangup(this.props.topic.name, this.props.seq); this.props.onHangup(this.props.topic, this.props.seq);
} }
toggleMedia(kind) { toggleMedia(kind) {
@@ -11434,26 +11435,23 @@ class CallPanel extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureCompon
} }
render() { render() {
if (!this.props.topic) {
return null;
}
const remoteActive = this.remoteRef.current && this.remoteRef.current.srcObject; const remoteActive = this.remoteRef.current && this.remoteRef.current.srcObject;
const audioIcon = this.state.localStream && this.state.localStream.getAudioTracks()[0].enabled ? 'mic' : 'mic_off'; 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 videoIcon = this.state.localStream && this.state.localStream.getVideoTracks()[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);
return react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { return react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
id: "video-container" id: "video-container"
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { }, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
id: "video-container-panel" id: "video-container-panel"
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { }, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: "video-elem self" className: "call-party self"
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("video", { }, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("video", {
ref: this.localRef, ref: this.localRef,
autoPlay: true, autoPlay: true,
muted: true, muted: true,
playsInline: true playsInline: true
}), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { }), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: "video-title" className: "title inactive"
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement(react_intl__WEBPACK_IMPORTED_MODULE_1__.FormattedMessage, { }, react__WEBPACK_IMPORTED_MODULE_0___default().createElement(react_intl__WEBPACK_IMPORTED_MODULE_1__.FormattedMessage, {
id: "calls_you_label", id: "calls_you_label",
defaultMessage: [{ defaultMessage: [{
@@ -11461,14 +11459,25 @@ class CallPanel extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureCompon
"value": "You" "value": "You"
}] }]
}))), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { }))), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: "video-elem peer" className: "call-party peer"
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("video", { }, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("video", {
ref: this.remoteRef, ref: this.remoteRef,
autoPlay: true, autoPlay: true,
playsInline: true playsInline: true
}), remoteActive ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { }), remoteActive ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: "video-title" className: "title inactive"
}, (0,_lib_utils_js__WEBPACK_IMPORTED_MODULE_5__.clipStr)(this.props.title, _config_js__WEBPACK_IMPORTED_MODULE_3__.MAX_TITLE_LENGTH)) : null)), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { }, peerTitle) : react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: "peer-card"
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: "avatar-box"
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_widgets_letter_tile_jsx__WEBPACK_IMPORTED_MODULE_2__["default"], {
tinode: this.props.tinode,
avatar: this.props.avatar,
topic: this.props.topic,
title: this.props.title
})), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: "title"
}, peerTitle)))), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
id: "video-container-controls" id: "video-container-controls"
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { }, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", {
className: "danger", className: "danger",
+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