feat(chat): Make chat push content to the side in large view
This commit is contained in:
parent
bf7aa39947
commit
5f5468995f
|
@ -4,16 +4,11 @@
|
|||
color: #FFF;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
/**
|
||||
* Make the sidebar flush with the top of the toolbar. Take the size of
|
||||
* the toolbar and subtract from 100%.
|
||||
*/
|
||||
height: calc(100% - #{$newToolbarSizeWithPadding});
|
||||
height: 100%;
|
||||
left: -$sidebarWidth;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
transition: left 0.5s;
|
||||
width: $sidebarWidth;
|
||||
z-index: $sideToolbarContainerZ;
|
||||
|
||||
|
|
|
@ -42,6 +42,11 @@
|
|||
display: none;
|
||||
}
|
||||
|
||||
&.shift-right {
|
||||
margin-left: $sidebarWidth;
|
||||
width: calc(100% - #{$sidebarWidth});
|
||||
}
|
||||
|
||||
.toolbox-background {
|
||||
background-image: linear-gradient(to top, rgba(0, 0, 0, 0.6), rgba(0, 0, 0, 0));
|
||||
transition: bottom .3s ease-in;
|
||||
|
|
|
@ -181,6 +181,13 @@
|
|||
visibility: hidden;
|
||||
z-index: $zindex2;
|
||||
}
|
||||
|
||||
&.shift-right {
|
||||
&#largeVideoContainer {
|
||||
margin-left: $sidebarWidth;
|
||||
width: calc(100% - #{$sidebarWidth});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#localVideoWrapper {
|
||||
|
|
|
@ -12,6 +12,7 @@ import {
|
|||
JitsiParticipantConnectionStatus
|
||||
} from '../../../react/features/base/lib-jitsi-meet';
|
||||
import { VIDEO_TYPE } from '../../../react/features/base/media';
|
||||
import { CHAT_SIZE } from '../../../react/features/chat';
|
||||
import {
|
||||
updateKnownLargeVideoResolution
|
||||
} from '../../../react/features/large-video';
|
||||
|
@ -323,7 +324,18 @@ export default class LargeVideoManager {
|
|||
* Update container size.
|
||||
*/
|
||||
updateContainerSize() {
|
||||
this.width = UIUtil.getAvailableVideoWidth();
|
||||
let widthToUse = UIUtil.getAvailableVideoWidth();
|
||||
const { isOpen } = APP.store.getState()['features/chat'];
|
||||
|
||||
if (isOpen) {
|
||||
/**
|
||||
* If chat state is open, we re-compute the container width
|
||||
* by subtracting the default width of the chat.
|
||||
*/
|
||||
widthToUse -= CHAT_SIZE;
|
||||
}
|
||||
|
||||
this.width = widthToUse;
|
||||
this.height = window.innerHeight;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
// @flow
|
||||
|
||||
import VideoLayout from '../../../modules/UI/videolayout/VideoLayout';
|
||||
|
||||
import {
|
||||
ADD_MESSAGE,
|
||||
CLEAR_MESSAGES,
|
||||
|
@ -86,14 +88,14 @@ export function setPrivateMessageRecipient(participant: Object) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Toggles display of the chat side panel.
|
||||
* Toggles display of the chat side panel while also taking window
|
||||
* resize into account.
|
||||
*
|
||||
* @returns {{
|
||||
* type: TOGGLE_CHAT
|
||||
* }}
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function toggleChat() {
|
||||
return {
|
||||
type: TOGGLE_CHAT
|
||||
return function(dispatch: (Object) => Object) {
|
||||
dispatch({ type: TOGGLE_CHAT });
|
||||
VideoLayout.onResize();
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
// @flow
|
||||
|
||||
import React from 'react';
|
||||
import Transition from 'react-transition-group/Transition';
|
||||
|
||||
import { translate } from '../../../base/i18n';
|
||||
import { Icon, IconClose } from '../../../base/icons';
|
||||
|
@ -84,11 +83,9 @@ class Chat extends AbstractChat<Props> {
|
|||
*/
|
||||
render() {
|
||||
return (
|
||||
<Transition
|
||||
in = { this.props._isOpen }
|
||||
timeout = { 500 }>
|
||||
{ this._renderPanelContent }
|
||||
</Transition>
|
||||
<>
|
||||
{ this._renderPanelContent() }
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -145,30 +142,25 @@ class Chat extends AbstractChat<Props> {
|
|||
);
|
||||
}
|
||||
|
||||
_renderPanelContent: (string) => React$Node | null;
|
||||
_renderPanelContent: () => React$Node | null;
|
||||
|
||||
/**
|
||||
* Renders the contents of the chat panel, depending on the current
|
||||
* animation state provided by {@code Transition}.
|
||||
* Renders the contents of the chat panel.
|
||||
*
|
||||
* @param {string} state - The current display transition state of the
|
||||
* {@code Chat} component, as provided by {@code Transition}.
|
||||
* @private
|
||||
* @returns {ReactElement | null}
|
||||
*/
|
||||
_renderPanelContent(state) {
|
||||
this._isExited = state === 'exited';
|
||||
|
||||
_renderPanelContent() {
|
||||
const { _isOpen, _showNamePrompt } = this.props;
|
||||
const ComponentToRender = !_isOpen && state === 'exited'
|
||||
? null
|
||||
: (
|
||||
const ComponentToRender = _isOpen
|
||||
? (
|
||||
<>
|
||||
{ this._renderChatHeader() }
|
||||
{ _showNamePrompt
|
||||
? <DisplayNameForm /> : this._renderChat() }
|
||||
</>
|
||||
);
|
||||
)
|
||||
: null;
|
||||
let className = '';
|
||||
|
||||
if (_isOpen) {
|
||||
|
|
|
@ -2,6 +2,11 @@
|
|||
|
||||
export const CHAT_VIEW_MODAL_ID = 'chatView';
|
||||
|
||||
/**
|
||||
* The size of the chat.
|
||||
*/
|
||||
export const CHAT_SIZE = 375;
|
||||
|
||||
/**
|
||||
* The audio ID of the audio element for which the {@link playAudio} action is
|
||||
* triggered when new chat message is received.
|
||||
|
|
|
@ -28,10 +28,8 @@ import {
|
|||
} from '../AbstractConference';
|
||||
import type { AbstractProps } from '../AbstractConference';
|
||||
|
||||
import InviteMore from './InviteMore';
|
||||
import Labels from './Labels';
|
||||
import { default as Notice } from './Notice';
|
||||
import { default as Subject } from './Subject';
|
||||
|
||||
declare var APP: Object;
|
||||
declare var config: Object;
|
||||
|
@ -201,8 +199,6 @@ class Conference extends AbstractConference<Props, *> {
|
|||
onMouseMove = { this._onShowToolbar }>
|
||||
|
||||
<Notice />
|
||||
<Subject />
|
||||
<InviteMore />
|
||||
<div id = 'videospace'>
|
||||
<LargeVideo />
|
||||
<KnockingParticipantList />
|
||||
|
|
|
@ -3,3 +3,5 @@
|
|||
export { default as Conference } from './Conference';
|
||||
export { default as renderConferenceTimer } from './ConferenceTimerDisplay';
|
||||
export { default as InsecureRoomNameLabel } from './InsecureRoomNameLabel';
|
||||
export { default as InviteMore } from './InviteMore';
|
||||
export { default as Subject } from './Subject';
|
||||
|
|
|
@ -4,6 +4,7 @@ import React, { Component } from 'react';
|
|||
|
||||
import { Watermarks } from '../../base/react';
|
||||
import { connect } from '../../base/redux';
|
||||
import { InviteMore, Subject } from '../../conference';
|
||||
import { fetchCustomBrandingData } from '../../dynamic-branding';
|
||||
import { Captions } from '../../subtitles/';
|
||||
|
||||
|
@ -26,6 +27,11 @@ type Props = {
|
|||
*/
|
||||
_fetchCustomBrandingData: Function,
|
||||
|
||||
/**
|
||||
* Prop that indicates whether the chat is open.
|
||||
*/
|
||||
_isChatOpen: boolean,
|
||||
|
||||
/**
|
||||
* Used to determine the value of the autoplay attribute of the underlying
|
||||
* video element.
|
||||
|
@ -57,12 +63,15 @@ class LargeVideo extends Component<Props> {
|
|||
*/
|
||||
render() {
|
||||
const style = this._getCustomSyles();
|
||||
const className = `videocontainer${this.props._isChatOpen ? ' shift-right' : ''}`;
|
||||
|
||||
return (
|
||||
<div
|
||||
className = 'videocontainer'
|
||||
className = { className }
|
||||
id = 'largeVideoContainer'
|
||||
style = { style }>
|
||||
<Subject />
|
||||
<InviteMore />
|
||||
<div id = 'sharedVideo'>
|
||||
<div id = 'sharedVideoIFrame' />
|
||||
</div>
|
||||
|
@ -133,10 +142,12 @@ class LargeVideo extends Component<Props> {
|
|||
function _mapStateToProps(state) {
|
||||
const testingConfig = state['features/base/config'].testing;
|
||||
const { backgroundColor, backgroundImageUrl } = state['features/dynamic-branding'];
|
||||
const { isOpen: isChatOpen } = state['features/chat'];
|
||||
|
||||
return {
|
||||
_customBackgroundColor: backgroundColor,
|
||||
_customBackgroundImageUrl: backgroundImageUrl,
|
||||
_isChatOpen: isChatOpen,
|
||||
_noAutoPlayVideo: testingConfig?.noAutoPlayVideo
|
||||
};
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ import { connect, equals } from '../../../base/redux';
|
|||
import { OverflowMenuItem } from '../../../base/toolbox';
|
||||
import { getLocalVideoTrack, toggleScreensharing } from '../../../base/tracks';
|
||||
import { VideoBlurButton } from '../../../blur';
|
||||
import { ChatCounter, toggleChat } from '../../../chat';
|
||||
import { CHAT_SIZE, ChatCounter, toggleChat } from '../../../chat';
|
||||
import { SharedDocumentButton } from '../../../etherpad';
|
||||
import { openFeedbackDialog } from '../../../feedback';
|
||||
import { beginAddPeople } from '../../../invite';
|
||||
|
@ -322,6 +322,10 @@ class Toolbox extends Component<Props, State> {
|
|||
this._onSetOverflowVisible(false);
|
||||
this.props.dispatch(setToolbarHovered(false));
|
||||
}
|
||||
|
||||
if (this.props._chatOpen !== prevProps._chatOpen) {
|
||||
this._onResize();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -344,9 +348,9 @@ class Toolbox extends Component<Props, State> {
|
|||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
const { _visible, _visibleButtons } = this.props;
|
||||
const { _chatOpen, _visible, _visibleButtons } = this.props;
|
||||
const rootClassNames = `new-toolbox ${_visible ? 'visible' : ''} ${
|
||||
_visibleButtons.size ? '' : 'no-buttons'}`;
|
||||
_visibleButtons.size ? '' : 'no-buttons'} ${_chatOpen ? 'shift-right' : ''}`;
|
||||
|
||||
return (
|
||||
<div
|
||||
|
@ -534,10 +538,15 @@ class Toolbox extends Component<Props, State> {
|
|||
* @returns {void}
|
||||
*/
|
||||
_onResize() {
|
||||
const width = window.innerWidth;
|
||||
let widthToUse = window.innerWidth;
|
||||
|
||||
if (this.state.windowWidth !== width) {
|
||||
this.setState({ windowWidth: width });
|
||||
// Take chat size into account when resizing toolbox.
|
||||
if (this.props._chatOpen) {
|
||||
widthToUse -= CHAT_SIZE;
|
||||
}
|
||||
|
||||
if (this.state.windowWidth !== widthToUse) {
|
||||
this.setState({ windowWidth: widthToUse });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1174,6 +1183,9 @@ class Toolbox extends Component<Props, State> {
|
|||
/ 2 // divide by the number of groups(left and right group)
|
||||
);
|
||||
|
||||
if (this._shouldShowButton('chat')) {
|
||||
buttonsLeft.push('chat');
|
||||
}
|
||||
if (this._shouldShowButton('desktop')
|
||||
&& this._isDesktopSharingButtonVisible()) {
|
||||
buttonsLeft.push('desktop');
|
||||
|
@ -1181,9 +1193,6 @@ class Toolbox extends Component<Props, State> {
|
|||
if (this._shouldShowButton('raisehand')) {
|
||||
buttonsLeft.push('raisehand');
|
||||
}
|
||||
if (this._shouldShowButton('chat')) {
|
||||
buttonsLeft.push('chat');
|
||||
}
|
||||
if (this._shouldShowButton('closedcaptions')) {
|
||||
buttonsLeft.push('closedcaptions');
|
||||
}
|
||||
|
@ -1239,15 +1248,6 @@ class Toolbox extends Component<Props, State> {
|
|||
return (
|
||||
<div className = 'toolbox-content'>
|
||||
<div className = 'button-group-left'>
|
||||
{ buttonsLeft.indexOf('desktop') !== -1
|
||||
&& this._renderDesktopSharingButton() }
|
||||
{ buttonsLeft.indexOf('raisehand') !== -1
|
||||
&& <ToolbarButton
|
||||
accessibilityLabel = { t('toolbar.accessibilityLabel.raiseHand') }
|
||||
icon = { IconRaisedHand }
|
||||
onClick = { this._onToolbarToggleRaiseHand }
|
||||
toggled = { _raisedHand }
|
||||
tooltip = { t('toolbar.raiseHand') } /> }
|
||||
{ buttonsLeft.indexOf('chat') !== -1
|
||||
&& <div className = 'toolbar-button-with-badge'>
|
||||
<ToolbarButton
|
||||
|
@ -1258,6 +1258,15 @@ class Toolbox extends Component<Props, State> {
|
|||
tooltip = { t('toolbar.chat') } />
|
||||
<ChatCounter />
|
||||
</div> }
|
||||
{ buttonsLeft.indexOf('desktop') !== -1
|
||||
&& this._renderDesktopSharingButton() }
|
||||
{ buttonsLeft.indexOf('raisehand') !== -1
|
||||
&& <ToolbarButton
|
||||
accessibilityLabel = { t('toolbar.accessibilityLabel.raiseHand') }
|
||||
icon = { IconRaisedHand }
|
||||
onClick = { this._onToolbarToggleRaiseHand }
|
||||
toggled = { _raisedHand }
|
||||
tooltip = { t('toolbar.raiseHand') } /> }
|
||||
{
|
||||
buttonsLeft.indexOf('closedcaptions') !== -1
|
||||
&& <ClosedCaptionButton />
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// @flow
|
||||
|
||||
import { hasAvailableDevices } from '../base/devices';
|
||||
import { isMobileBrowser } from '../base/environment/utils';
|
||||
|
||||
declare var interfaceConfig: Object;
|
||||
|
||||
|
@ -43,8 +44,10 @@ export function isToolboxVisible(state: Object) {
|
|||
visible
|
||||
} = state['features/toolbox'];
|
||||
const { audioSettingsVisible, videoSettingsVisible } = state['features/settings'];
|
||||
const { isOpen } = state['features/chat'];
|
||||
const isMobileChatOpen = isMobileBrowser() && isOpen;
|
||||
|
||||
return Boolean(!iAmSipGateway && (timeoutID || visible || alwaysVisible
|
||||
return Boolean(!isMobileChatOpen && !iAmSipGateway && (timeoutID || visible || alwaysVisible
|
||||
|| audioSettingsVisible || videoSettingsVisible));
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue