WIP: the new call views work now
just need to add the buttons and then get rid of the status barpull/21833/head
parent
b3a80d5a50
commit
c921567831
|
@ -41,7 +41,7 @@ limitations under the License.
|
|||
|
||||
.mx_BaseAvatar_image {
|
||||
object-fit: cover;
|
||||
border-radius: 40px;
|
||||
border-radius: 125px;
|
||||
vertical-align: top;
|
||||
background-color: $avatar-bg-color;
|
||||
}
|
||||
|
|
|
@ -15,47 +15,46 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
.mx_CallView {
|
||||
border-radius: 10px;
|
||||
background-color: $input-lighter-bg-color;
|
||||
padding-left: 8px;
|
||||
padding-right: 8px;
|
||||
// XXX: CallContainer sets pointer-events: none - should probably be set back in a better place
|
||||
pointer-events: initial;
|
||||
}
|
||||
|
||||
.mx_CallView_large {
|
||||
padding-bottom: 10px;
|
||||
|
||||
.mx_CallView_voice {
|
||||
height: 360px;
|
||||
}
|
||||
}
|
||||
|
||||
.mx_CallView_pip {
|
||||
width: 320px;
|
||||
|
||||
.mx_CallView_voice {
|
||||
height: 180px
|
||||
}
|
||||
}
|
||||
|
||||
.mx_CallView_voice {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: $inverted-bg-color;
|
||||
}
|
||||
|
||||
/*
|
||||
.mx_CallView_voice {
|
||||
background-color: $accent-color;
|
||||
color: $accent-fg-color;
|
||||
cursor: pointer;
|
||||
padding: 6px;
|
||||
font-weight: bold;
|
||||
|
||||
border-radius: 8px;
|
||||
min-width: 200px;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
img {
|
||||
margin: 4px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
> div {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
// Hacky vertical align
|
||||
padding-top: 3px;
|
||||
}
|
||||
|
||||
> div > p,
|
||||
> div > h1 {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-size: $font-13px;
|
||||
line-height: $font-15px;
|
||||
}
|
||||
|
||||
> div > p {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
> * {
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.mx_CallView_hangup {
|
||||
|
@ -92,6 +91,7 @@ limitations under the License.
|
|||
background-color: $primary-fg-color;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
.mx_CallView_video {
|
||||
width: 100%;
|
||||
|
@ -99,3 +99,76 @@ limitations under the License.
|
|||
z-index: 30;
|
||||
}
|
||||
|
||||
.mx_CallView_header {
|
||||
height: 44px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: left;
|
||||
|
||||
.mx_BaseAvatar {
|
||||
margin-right: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.mx_CallView_header_callType {
|
||||
font-weight: bold;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.mx_CallView_header_controls {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.mx_CallView_header_control_fullscreen {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
cursor: pointer;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
vertical-align: middle;
|
||||
background-color: $secondary-fg-color;
|
||||
mask-repeat: no-repeat;
|
||||
mask-size: contain;
|
||||
mask-position: center;
|
||||
mask-image: url('$(res)/img/element-icons/call/fullscreen.svg');
|
||||
}
|
||||
}
|
||||
|
||||
.mx_CallView_header_roomName {
|
||||
font-weight: bold;
|
||||
font-size: 12px;
|
||||
line-height: initial;
|
||||
}
|
||||
|
||||
.mx_CallView_header_callTypeSmall {
|
||||
font-size: 12px;
|
||||
color: $secondary-fg-color;
|
||||
line-height: initial;
|
||||
}
|
||||
|
||||
.mx_CallView_header_phoneIcon {
|
||||
display: inline-block;
|
||||
margin-right: 6px;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
vertical-align: middle;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
background-color: $warning-color;
|
||||
mask-repeat: no-repeat;
|
||||
mask-size: contain;
|
||||
mask-position: center;
|
||||
mask-image: url('$(res)/img/element-icons/call/voice-call.svg');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,9 +14,9 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
.mx_VideoFeed video {
|
||||
/*.mx_VideoFeed video {
|
||||
width: 100%;
|
||||
}
|
||||
}*/
|
||||
|
||||
.mx_VideoFeed_remote {
|
||||
width: 100%;
|
||||
|
@ -28,16 +28,17 @@ limitations under the License.
|
|||
width: 25%;
|
||||
height: 25%;
|
||||
position: absolute;
|
||||
left: 10px;
|
||||
bottom: 10px;
|
||||
right: 10px;
|
||||
top: 10px;
|
||||
z-index: 100;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.mx_VideoFeed_local video {
|
||||
/*.mx_VideoFeed_local video {
|
||||
width: auto;
|
||||
height: 100%;
|
||||
}
|
||||
}*/
|
||||
|
||||
.mx_VideoFeed_mirror video {
|
||||
.mx_VideoFeed_mirror {
|
||||
transform: scale(-1, 1);
|
||||
}
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
/*
|
||||
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
interface IProps {
|
||||
}
|
||||
|
||||
const PulsedAvatar: React.FC<IProps> = (props) => {
|
||||
return <div className="mx_PulsedAvatar">
|
||||
{props.children}
|
||||
</div>;
|
||||
};
|
||||
|
||||
export default PulsedAvatar;
|
|
@ -26,6 +26,15 @@ import PersistentApp from "../elements/PersistentApp";
|
|||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
import { CallState, MatrixCall } from 'matrix-js-sdk/src/webrtc/call';
|
||||
|
||||
const SHOW_CALL_IN_STATES = [
|
||||
CallState.Connected,
|
||||
CallState.InviteSent,
|
||||
CallState.Connecting,
|
||||
CallState.CreateAnswer,
|
||||
CallState.CreateOffer,
|
||||
CallState.WaitLocalMedia,
|
||||
];
|
||||
|
||||
interface IProps {
|
||||
}
|
||||
|
||||
|
@ -94,14 +103,13 @@ export default class CallPreview extends React.Component<IProps, IState> {
|
|||
const callForRoom = CallHandler.sharedInstance().getCallForRoom(this.state.roomId);
|
||||
const showCall = (
|
||||
this.state.activeCall &&
|
||||
this.state.activeCall.state === CallState.Connected &&
|
||||
SHOW_CALL_IN_STATES.includes(this.state.activeCall.state) &&
|
||||
!callForRoom
|
||||
);
|
||||
|
||||
if (showCall) {
|
||||
return (
|
||||
<CallView
|
||||
className="mx_CallPreview"
|
||||
onClick={this.onCallViewClick}
|
||||
showHangup={true}
|
||||
/>
|
||||
|
|
|
@ -21,10 +21,8 @@ import dis from '../../../dispatcher/dispatcher';
|
|||
import CallHandler from '../../../CallHandler';
|
||||
import {MatrixClientPeg} from '../../../MatrixClientPeg';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import AccessibleButton from '../elements/AccessibleButton';
|
||||
import VideoFeed, { VideoFeedType } from "./VideoFeed";
|
||||
import RoomAvatar from "../avatars/RoomAvatar";
|
||||
import PulsedAvatar from '../avatars/PulsedAvatar';
|
||||
import { CallState, CallType, MatrixCall } from 'matrix-js-sdk/src/webrtc/call';
|
||||
import { CallEvent } from 'matrix-js-sdk/src/webrtc/call';
|
||||
|
||||
|
@ -43,9 +41,6 @@ interface IProps {
|
|||
// in a way that is likely to cause a resize.
|
||||
onResize?: any;
|
||||
|
||||
// classname applied to view,
|
||||
className?: string;
|
||||
|
||||
// Whether to show the hang up icon:W
|
||||
showHangup?: boolean;
|
||||
}
|
||||
|
@ -85,7 +80,7 @@ function exitFullscreen() {
|
|||
|
||||
export default class CallView extends React.Component<IProps, IState> {
|
||||
private dispatcherRef: string;
|
||||
private container = createRef<HTMLDivElement>();
|
||||
private contentRef = createRef<HTMLDivElement>();
|
||||
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
@ -111,11 +106,11 @@ export default class CallView extends React.Component<IProps, IState> {
|
|||
private onAction = (payload) => {
|
||||
switch (payload.action) {
|
||||
case 'video_fullscreen': {
|
||||
if (!this.container.current) {
|
||||
if (!this.contentRef.current) {
|
||||
return;
|
||||
}
|
||||
if (payload.fullscreen) {
|
||||
requestFullscreen(this.container.current);
|
||||
requestFullscreen(this.contentRef.current);
|
||||
} else if (getFullScreenElement()) {
|
||||
exitFullscreen();
|
||||
}
|
||||
|
@ -144,11 +139,6 @@ export default class CallView extends React.Component<IProps, IState> {
|
|||
if (this.props.room) {
|
||||
const roomId = this.props.room.roomId;
|
||||
call = CallHandler.sharedInstance().getCallForRoom(roomId);
|
||||
|
||||
// We don't currently show voice calls in this view when in the room:
|
||||
// they're represented in the room status bar at the bottom instead
|
||||
// (but this will all change with the new designs)
|
||||
if (call && call.type == CallType.Voice) call = null;
|
||||
} else {
|
||||
call = CallHandler.sharedInstance().getAnyActiveCall();
|
||||
// Ignore calls if we can't get the room associated with them.
|
||||
|
@ -160,7 +150,7 @@ export default class CallView extends React.Component<IProps, IState> {
|
|||
}
|
||||
}
|
||||
|
||||
if (call && call.state == CallState.Ended) return null;
|
||||
if (call && [CallState.Ended, CallState.Ringing].includes(call.state)) return null;
|
||||
return call;
|
||||
}
|
||||
|
||||
|
@ -177,51 +167,91 @@ export default class CallView extends React.Component<IProps, IState> {
|
|||
});
|
||||
};
|
||||
|
||||
private onFullscreenClick = () => {
|
||||
dis.dispatch({
|
||||
action: 'video_fullscreen',
|
||||
fullscreen: true,
|
||||
});
|
||||
};
|
||||
|
||||
public render() {
|
||||
let view: React.ReactNode;
|
||||
if (!this.state.call) return null;
|
||||
|
||||
if (this.state.call) {
|
||||
if (this.state.call.type === "voice") {
|
||||
const client = MatrixClientPeg.get();
|
||||
const callRoom = client.getRoom(this.state.call.roomId);
|
||||
const client = MatrixClientPeg.get();
|
||||
const callRoom = client.getRoom(this.state.call.roomId);
|
||||
|
||||
let caption = _t("Active call");
|
||||
if (this.state.isLocalOnHold) {
|
||||
// we currently have no UI for holding / unholding a call (apart from slash
|
||||
// commands) so we don't disintguish between when we've put the call on hold
|
||||
// (ie. we'd show an unhold button) and when the other side has put us on hold
|
||||
// (where obviously we would not show such a button).
|
||||
caption = _t("Call Paused");
|
||||
}
|
||||
//const callControls = <div className="mx_CallView_callControls">
|
||||
|
||||
view = <AccessibleButton className="mx_CallView_voice" onClick={this.props.onClick}>
|
||||
<PulsedAvatar>
|
||||
<RoomAvatar
|
||||
room={callRoom}
|
||||
height={35}
|
||||
width={35}
|
||||
/>
|
||||
</PulsedAvatar>
|
||||
<div>
|
||||
<h1>{callRoom.name}</h1>
|
||||
<p>{ caption }</p>
|
||||
</div>
|
||||
</AccessibleButton>;
|
||||
} else {
|
||||
// For video calls, we currently ignore the call hold state altogether
|
||||
// (the video will just go black)
|
||||
//</div>;
|
||||
|
||||
// if we're fullscreen, we don't want to set a maxHeight on the video element.
|
||||
const maxVideoHeight = getFullScreenElement() ? null : this.props.maxVideoHeight;
|
||||
view = <div className="mx_CallView_video" onClick={this.props.onClick}>
|
||||
<VideoFeed type={VideoFeedType.Remote} call={this.state.call} onResize={this.props.onResize}
|
||||
maxHeight={maxVideoHeight}
|
||||
/>
|
||||
<VideoFeed type={VideoFeedType.Local} call={this.state.call} />
|
||||
</div>;
|
||||
}
|
||||
// The 'content' for the call, ie. the videos for a video call and profile picture
|
||||
// for voice calls (fills the bg)
|
||||
let contentView: React.ReactNode;
|
||||
|
||||
if (this.state.call.type === CallType.Video) {
|
||||
// if we're fullscreen, we don't want to set a maxHeight on the video element.
|
||||
const maxVideoHeight = getFullScreenElement() ? null : this.props.maxVideoHeight;
|
||||
contentView = <div className="mx_CallView_video" ref={this.contentRef}>
|
||||
<VideoFeed type={VideoFeedType.Remote} call={this.state.call} onResize={this.props.onResize}
|
||||
maxHeight={maxVideoHeight}
|
||||
/>
|
||||
<VideoFeed type={VideoFeedType.Local} call={this.state.call} />
|
||||
</div>;
|
||||
} else {
|
||||
const avatarSize = this.props.room ? 200 : 75;
|
||||
contentView = <div className="mx_CallView_voice">
|
||||
<RoomAvatar
|
||||
room={callRoom}
|
||||
height={avatarSize}
|
||||
width={avatarSize}
|
||||
/>
|
||||
</div>;
|
||||
}
|
||||
|
||||
/*
|
||||
if (!this.props.room) {
|
||||
const client = MatrixClientPeg.get();
|
||||
const callRoom = client.getRoom(this.state.call.roomId);
|
||||
|
||||
let caption = _t("Active call");
|
||||
if (this.state.isLocalOnHold) {
|
||||
// we currently have no UI for holding / unholding a call (apart from slash
|
||||
// commands) so we don't disintguish between when we've put the call on hold
|
||||
// (ie. we'd show an unhold button) and when the other side has put us on hold
|
||||
// (where obviously we would not show such a button).
|
||||
caption = _t("Call Paused");
|
||||
}
|
||||
|
||||
view = <AccessibleButton className="mx_CallView_voice" onClick={this.props.onClick}>
|
||||
<PulsedAvatar>
|
||||
<RoomAvatar
|
||||
room={callRoom}
|
||||
height={35}
|
||||
width={35}
|
||||
/>
|
||||
</PulsedAvatar>
|
||||
<div>
|
||||
<h1>{callRoom.name}</h1>
|
||||
<p>{ caption }</p>
|
||||
</div>
|
||||
</AccessibleButton>;
|
||||
} else {
|
||||
// For video calls, we currently ignore the call hold state altogether
|
||||
// (the video will just go black)
|
||||
|
||||
// if we're fullscreen, we don't want to set a maxHeight on the video element.
|
||||
const maxVideoHeight = getFullScreenElement() ? null : this.props.maxVideoHeight;
|
||||
view = <div className="mx_CallView_video" onClick={this.props.onClick}>
|
||||
<VideoFeed type={VideoFeedType.Remote} call={this.state.call} onResize={this.props.onResize}
|
||||
maxHeight={maxVideoHeight}
|
||||
/>
|
||||
<VideoFeed type={VideoFeedType.Local} call={this.state.call} />
|
||||
</div>;
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
let hangup: React.ReactNode;
|
||||
if (this.props.showHangup) {
|
||||
hangup = <div
|
||||
|
@ -234,10 +264,45 @@ export default class CallView extends React.Component<IProps, IState> {
|
|||
}}
|
||||
/>;
|
||||
}
|
||||
*/
|
||||
|
||||
return <div className={this.props.className} ref={this.container}>
|
||||
{view}
|
||||
{hangup}
|
||||
const callTypeText = this.state.call.type === CallType.Video ? _t("Video Call") : _t("Voice Call");
|
||||
let myClassName;
|
||||
|
||||
let fullScreenButton;
|
||||
if (this.state.call.type === CallType.Video) {
|
||||
fullScreenButton = <div className="mx_CallView_header_control_fullscreen"
|
||||
onClick={this.onFullscreenClick} title={_t("Fill screen")}
|
||||
/>;
|
||||
}
|
||||
|
||||
const headerControls = <div className="mx_CallView_header_controls">
|
||||
{fullScreenButton}
|
||||
</div>;
|
||||
|
||||
let header: React.ReactNode;
|
||||
if (this.props.room) {
|
||||
header = <div className="mx_CallView_header">
|
||||
<div className="mx_CallView_header_phoneIcon"></div>
|
||||
<span className="mx_CallView_header_callType">{callTypeText}</span>
|
||||
{headerControls}
|
||||
</div>;
|
||||
myClassName = 'mx_CallView_large';
|
||||
} else {
|
||||
header = <div className="mx_CallView_header">
|
||||
<RoomAvatar room={callRoom} height={32} width={32} />
|
||||
<div>
|
||||
<div className="mx_CallView_header_roomName">{callRoom.name}</div>
|
||||
<div className="mx_CallView_header_callTypeSmall">{callTypeText}</div>
|
||||
</div>
|
||||
{headerControls}
|
||||
</div>;
|
||||
myClassName = 'mx_CallView_pip';
|
||||
}
|
||||
|
||||
return <div className={"mx_CallView " + myClassName}>
|
||||
{header}
|
||||
{contentView}
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,7 +22,6 @@ import dis from '../../../dispatcher/dispatcher';
|
|||
import { _t } from '../../../languageHandler';
|
||||
import { ActionPayload } from '../../../dispatcher/payloads';
|
||||
import CallHandler from '../../../CallHandler';
|
||||
import PulsedAvatar from '../avatars/PulsedAvatar';
|
||||
import RoomAvatar from '../avatars/RoomAvatar';
|
||||
import FormButton from '../elements/FormButton';
|
||||
import { CallState } from 'matrix-js-sdk/lib/webrtc/call';
|
||||
|
@ -108,13 +107,11 @@ export default class IncomingCallBox extends React.Component<IProps, IState> {
|
|||
|
||||
return <div className="mx_IncomingCallBox">
|
||||
<div className="mx_IncomingCallBox_CallerInfo">
|
||||
<PulsedAvatar>
|
||||
<RoomAvatar
|
||||
room={room}
|
||||
height={32}
|
||||
width={32}
|
||||
/>
|
||||
</PulsedAvatar>
|
||||
<RoomAvatar
|
||||
room={room}
|
||||
height={32}
|
||||
width={32}
|
||||
/>
|
||||
<div>
|
||||
<h1>{caller}</h1>
|
||||
<p>{incomingCallText}</p>
|
||||
|
|
|
@ -73,8 +73,6 @@ export default class VideoFeed extends React.Component<IProps> {
|
|||
let videoStyle = {};
|
||||
if (this.props.maxHeight) videoStyle = { maxHeight: this.props.maxHeight };
|
||||
|
||||
return <div className={classnames(videoClasses)}>
|
||||
<video ref={this.vid} style={videoStyle}></video>
|
||||
</div>;
|
||||
return <video className={classnames(videoClasses)} ref={this.vid} style={videoStyle} />;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -529,8 +529,8 @@
|
|||
"When rooms are upgraded": "When rooms are upgraded",
|
||||
"My Ban List": "My Ban List",
|
||||
"This is your list of users/servers you have blocked - don't leave the room!": "This is your list of users/servers you have blocked - don't leave the room!",
|
||||
"Active call": "Active call",
|
||||
"Call Paused": "Call Paused",
|
||||
"Video Call": "Video Call",
|
||||
"Voice Call": "Voice Call",
|
||||
"Unknown caller": "Unknown caller",
|
||||
"Incoming voice call": "Incoming voice call",
|
||||
"Incoming video call": "Incoming video call",
|
||||
|
@ -2123,6 +2123,7 @@
|
|||
"%(count)s <resendText>Resend all</resendText> or <cancelText>cancel all</cancelText> now. You can also select individual messages to resend or cancel.|one": "<resendText>Resend message</resendText> or <cancelText>cancel message</cancelText> now.",
|
||||
"Calling...": "Calling...",
|
||||
"Call connecting...": "Call connecting...",
|
||||
"Active call": "Active call",
|
||||
"Starting camera...": "Starting camera...",
|
||||
"Starting microphone...": "Starting microphone...",
|
||||
"Connectivity to the server has been lost.": "Connectivity to the server has been lost.",
|
||||
|
|
Loading…
Reference in New Issue