allow messaging while call is in progress (same topic only for now)

This commit is contained in:
or-else
2025-10-08 16:49:45 +03:00
parent abf5b19758
commit 1085d1caf6
9 changed files with 382 additions and 100 deletions
+1 -1
View File
@@ -130,7 +130,7 @@ export default class CallIncoming extends React.Component {
<div className="caller-name">{truncateString(this.state.fullName, MAX_PEER_TITLE_LENGTH)}
<BadgeList short={true} trustedBadges={this.state.trustedBadges} /></div>
</div>
<div className="controls">
<div id="controls">
{this.props.callState == CALL_STATE_INCOMING_RECEIVED ?
<>
<button className="danger" onClick={this.handleRejectCall}>
+106 -41
View File
@@ -52,8 +52,10 @@ class CallPanel extends React.PureComponent {
};
this.isOutgoingCall = props.callState == CALL_STATE_OUTGOING_INITATED;
this.containerRef = React.createRef();
this.localRef = React.createRef();
this.remoteRef = React.createRef();
// Cache for remote ice candidates until initial setup gets completed.
this.remoteIceCandidatesCache = [];
@@ -100,6 +102,53 @@ class CallPanel extends React.PureComponent {
this.handleDataChannelMessage = this.handleDataChannelMessage.bind(this);
this.handleDataChannelOpen = this.handleDataChannelOpen.bind(this);
this.handleDataChannelClose = this.handleDataChannelClose.bind(this);
this.bounds = {};
this.dragStart = (event) => {
if (!event.target.classList.contains('draggable')) {
// Prevent dragging when clicking on buttons or other non-draggable elements.
return;
}
if (window.matchMedia('(width<=640px)')) {
// No dragging on small screens.
return;
}
if (this.props.minimized && this.containerRef) {
event.stopPropagation();
this.containerRef.current.setPointerCapture(event.pointerId);
this.containerRef.current.style.cursor = 'grabbing';
this.bounds.left = this.containerRef.current.offsetLeft;
this.bounds.top = this.containerRef.current.offsetTop;
this.bounds.width = this.containerRef.current.offsetWidth;
this.bounds.height = this.containerRef.current.offsetHeight;
const parent = this.containerRef.current.offsetParent;
this.bounds.maxX = parent.offsetWidth;
this.bounds.maxY = parent.offsetHeight;
}
};
this.drag = (event) => {
event.stopPropagation();
const el = this.containerRef.current;
if (this.props.minimized && this.containerRef && el.hasPointerCapture(event.pointerId)) {
// Calculate new position. Restrict to the visible area of the parent element.
this.bounds.left = Math.min(Math.max(this.bounds.left + event.movementX, 0),
this.bounds.maxX - this.bounds.width);
this.bounds.top = Math.min(Math.max(this.bounds.top + event.movementY, 0),
this.bounds.maxY - this.bounds.height);
el.style.left = `${this.bounds.left}px`;
el.style.top = `${this.bounds.top}px`;
}
};
this.dragEnd = (event) => {
event.stopPropagation();
if (this.props.minimized && this.containerRef) {
this.containerRef.current.releasePointerCapture(event.pointerId);
this.containerRef.current.style.cursor = 'grab';
}
};
}
componentDidMount() {
@@ -110,12 +159,24 @@ class CallPanel extends React.PureComponent {
this.props.callState == CALL_STATE_IN_PROGRESS) && this.localRef.current) {
this.start();
}
if (this.containerRef) {
this.containerRef.current.addEventListener('pointerdown', this.dragStart);
this.containerRef.current.addEventListener('pointermove', this.drag);
this.containerRef.current.addEventListener('pointerup', this.dragEnd);
}
}
componentWillUnmount() {
const topic = this.props.tinode.getTopic(this.props.topic);
topic.onInfo = this.previousOnInfo;
this.stop();
if (this.containerRef) {
this.containerRef.current.removeEventListener('pointerdown', this.dragStart);
this.containerRef.current.removeEventListener('pointermove', this.drag);
this.containerRef.current.removeEventListener('pointerup', this.dragEnd);
}
}
handleVideoCallAccepted(info) {
@@ -595,52 +656,56 @@ class CallPanel extends React.PureComponent {
}
}
const minimizedClass = this.props.minimized ? 'minimized' : null;
const fullScreen = this.props.minimized ? 'fullscreen' : 'fullscreen_exit';
return (
<>
<div id="video-container">
<div id="video-container-panel">
<div className="call-party self" disabled={this.state.audioOnly}>
<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" disabled={!remoteActive}>
{remoteActive ?
<>
<video ref={this.remoteRef} autoPlay playsInline />
<div className="caller-name inactive">{peerTitle}</div>
</> :
<>
<audio ref={this.remoteRef} autoPlay />
<div className={`caller-card${pulseAnimation}`}>
<div className="avatar-box">
<LetterTile
tinode={this.props.tinode}
avatar={this.props.avatar}
topic={this.props.topic}
title={this.props.title} />
</div>
<div className="caller-name">{peerTitle}</div>
</div>
</>
}
<div id="video-container" className={minimizedClass} ref={this.containerRef}>
<div id="video-container-panel">
<div className="call-party self" disabled={this.state.audioOnly || this.props.minimized}>
<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="controls">
<button className="danger" onClick={this.handleCloseClick}>
<i className="material-icons">call_end</i>
</button>
<button className="secondary" onClick={this.handleToggleCameraClick} disabled={disabled}>
<i className="material-icons">{videoIcon}</i>
</button>
<button className="secondary" onClick={this.handleToggleMicClick} disabled={disabled}>
<i className="material-icons">{audioIcon}</i>
</button>
<div className="call-party peer" disabled={!remoteActive}>
{remoteActive ?
<>
<video ref={this.remoteRef} autoPlay playsInline />
<div className="caller-name inactive">{peerTitle}</div>
</> :
<>
<audio ref={this.remoteRef} autoPlay />
<div className={`caller-card${pulseAnimation} draggable`}>
<div className="avatar-box">
<LetterTile
tinode={this.props.tinode}
avatar={this.props.avatar}
topic={this.props.topic}
title={this.props.title} />
</div>
<div className="caller-name">{peerTitle}</div>
</div>
</>
}
</div>
</div>
</>
<button className="full-screen" onClick={this.props.onToggleMinimize}>
<i className="material-icons">{fullScreen}</i>
</button>
<div id="controls" className={minimizedClass}>
<button className="danger" onClick={this.handleCloseClick}>
<i className="material-icons">call_end</i>
</button>
<button className="secondary" onClick={this.handleToggleCameraClick} disabled={disabled}>
<i className="material-icons">{videoIcon}</i>
</button>
<button className="secondary" onClick={this.handleToggleMicClick} disabled={disabled}>
<i className="material-icons">{audioIcon}</i>
</button>
</div>
</div>
);
}
};