Handle narrow layouts

Signed-off-by: Šimon Brandner <simon.bra.ag@gmail.com>
This commit is contained in:
Šimon Brandner 2021-08-05 11:47:58 +02:00
parent 4dda4b241a
commit 9e4f5719a4
No known key found for this signature in database
GPG key ID: CC823428E9B582FB
2 changed files with 218 additions and 143 deletions

View file

@ -14,7 +14,13 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
.mx_CallEvent_wrapper {
display: flex;
justify-content: center;
width: 100%;
.mx_CallEvent {
position: relative;
display: flex;
flex-direction: row;
align-items: center;
@ -22,11 +28,34 @@ limitations under the License.
background-color: $dark-panel-bg-color;
border-radius: 8px;
margin: 10px auto;
width: 75%;
box-sizing: border-box;
height: 60px;
.mx_CallEvent_iconButton {
display: inline-flex;
margin-right: 8px;
&::before {
content: '';
height: 16px;
width: 16px;
background-color: $tertiary-fg-color;
mask-repeat: no-repeat;
mask-size: contain;
mask-position: center;
}
}
.mx_CallEvent_silence::before {
mask-image: url('$(res)/img/voip/silence.svg');
}
.mx_CallEvent_unSilence::before {
mask-image: url('$(res)/img/voip/un-silence.svg');
}
&.mx_CallEvent_voice {
.mx_CallEvent_type_icon::before,
.mx_CallEvent_content_button_callBack span::before,
@ -134,29 +163,38 @@ limitations under the License.
.mx_CallEvent_content_tooltip {
margin-right: 5px;
}
}
&.mx_CallEvent_narrow {
height: unset;
width: 290px;
flex-direction: column;
align-items: unset;
gap: 16px;
.mx_CallEvent_iconButton {
display: inline-flex;
margin-right: 8px;
&::before {
content: '';
position: absolute;
margin-right: 0;
top: 12px;
right: 12px;
height: 16px;
width: 16px;
background-color: $tertiary-fg-color;
mask-repeat: no-repeat;
mask-size: contain;
mask-position: center;
display: flex;
}
.mx_CallEvent_info {
margin-top: 12px;
margin-right: 12px;
.mx_CallEvent_sender {
margin-bottom: 8px;
}
}
.mx_CallEvent_silence::before {
mask-image: url('$(res)/img/voip/silence.svg');
}
.mx_CallEvent_unSilence::before {
mask-image: url('$(res)/img/voip/un-silence.svg');
.mx_CallEvent_content {
margin-left: 54px; // mx_CallEvent margin (12px) + avatar (32px) + mx_CallEvent_info_basic margin (10px)
margin-bottom: 16px;
}
}
}
}

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import React, { createRef } from 'react';
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import { _t, _td } from '../../../languageHandler';
@ -26,6 +26,8 @@ import InfoTooltip, { InfoTooltipKind } from '../elements/InfoTooltip';
import classNames from 'classnames';
import AccessibleTooltipButton from '../elements/AccessibleTooltipButton';
const MAX_NON_NARROW_WIDTH = 400 / 70 * 100;
interface IProps {
mxEvent: MatrixEvent;
callEventGrouper: CallEventGrouper;
@ -34,6 +36,7 @@ interface IProps {
interface IState {
callState: CallState | CustomCallState;
silenced: boolean;
narrow: boolean;
}
const TEXTUAL_STATES: Map<CallState | CustomCallState, string> = new Map([
@ -41,26 +44,42 @@ const TEXTUAL_STATES: Map<CallState | CustomCallState, string> = new Map([
[CallState.Connecting, _td("Connecting")],
]);
export default class CallEvent extends React.Component<IProps, IState> {
export default class CallEvent extends React.PureComponent<IProps, IState> {
private wrapperElement = createRef<HTMLDivElement>();
private resizeObserver: ResizeObserver;
constructor(props: IProps) {
super(props);
this.state = {
callState: this.props.callEventGrouper.state,
silenced: false,
narrow: false,
};
}
componentDidMount() {
this.props.callEventGrouper.addListener(CallEventGrouperEvent.StateChanged, this.onStateChanged);
this.props.callEventGrouper.addListener(CallEventGrouperEvent.SilencedChanged, this.onSilencedChanged);
this.resizeObserver = new ResizeObserver(this.resizeObserverCallback);
this.resizeObserver.observe(this.wrapperElement.current);
}
componentWillUnmount() {
this.props.callEventGrouper.removeListener(CallEventGrouperEvent.StateChanged, this.onStateChanged);
this.props.callEventGrouper.removeListener(CallEventGrouperEvent.SilencedChanged, this.onSilencedChanged);
this.resizeObserver.disconnect();
}
private resizeObserverCallback = (entries: ResizeObserverEntry[]): void => {
const wrapperElementEntry = entries.find((entry) => entry.target === this.wrapperElement.current);
if (!wrapperElementEntry) return;
this.setState({ narrow: wrapperElementEntry.contentRect.width < MAX_NON_NARROW_WIDTH });
};
private onSilencedChanged = (newState) => {
this.setState({ silenced: newState });
};
@ -81,8 +100,7 @@ export default class CallEvent extends React.Component<IProps, IState> {
);
}
private renderContent(state: CallState | CustomCallState): JSX.Element {
if (state === CallState.Ringing) {
private renderSilenceIcon(): JSX.Element {
const silenceClass = classNames({
"mx_CallEvent_iconButton": true,
"mx_CallEvent_unSilence": this.state.silenced,
@ -90,12 +108,24 @@ export default class CallEvent extends React.Component<IProps, IState> {
});
return (
<div className="mx_CallEvent_content">
<AccessibleTooltipButton
className={silenceClass}
onClick={this.props.callEventGrouper.toggleSilenced}
title={this.state.silenced ? _t("Sound on") : _t("Silence call")}
/>
);
}
private renderContent(state: CallState | CustomCallState): JSX.Element {
if (state === CallState.Ringing) {
let silenceIcon;
if (!this.state.narrow) {
silenceIcon = this.renderSilenceIcon();
}
return (
<div className="mx_CallEvent_content">
{ silenceIcon }
<AccessibleButton
className="mx_CallEvent_content_button mx_CallEvent_content_button_reject"
onClick={this.props.callEventGrouper.rejectCall}
@ -209,18 +239,24 @@ export default class CallEvent extends React.Component<IProps, IState> {
const callState = this.state.callState;
const hangupReason = this.props.callEventGrouper.hangupReason;
const content = this.renderContent(callState);
const className = classNames({
mx_CallEvent: true,
const className = classNames("mx_CallEvent", {
mx_CallEvent_voice: isVoice,
mx_CallEvent_video: !isVoice,
mx_CallEvent_narrow: this.state.narrow,
mx_CallEvent_missed: (
callState === CustomCallState.Missed ||
(callState === CallState.Ended && hangupReason === CallErrorCode.InviteTimeout)
),
});
let silenceIcon;
if (this.state.narrow && this.state.callState === CallState.Ringing) {
silenceIcon = this.renderSilenceIcon();
}
return (
<div className="mx_CallEvent_wrapper" ref={this.wrapperElement}>
<div className={className}>
{ silenceIcon }
<div className="mx_CallEvent_info">
<MemberAvatar
member={event.sender}
@ -239,6 +275,7 @@ export default class CallEvent extends React.Component<IProps, IState> {
</div>
{ content }
</div>
</div>
);
}
}