diff --git a/bigbluebutton-html5/imports/startup/client/base.jsx b/bigbluebutton-html5/imports/startup/client/base.jsx index 809394a70fb5ec1ff6d8f92f793d9e155dd033ea..f63f29d2e9a72dc66c39a3653c0675fb8be8dfc2 100755 --- a/bigbluebutton-html5/imports/startup/client/base.jsx +++ b/bigbluebutton-html5/imports/startup/client/base.jsx @@ -11,6 +11,7 @@ import logger from '/imports/startup/client/logger'; import Users from '/imports/api/users'; import { Session } from 'meteor/session'; import { FormattedMessage } from 'react-intl'; +import { Meteor } from 'meteor/meteor'; import Meetings, { RecordMeetings } from '../../api/meetings'; import AppService from '/imports/ui/components/app/service'; import Breakouts from '/imports/api/breakouts'; @@ -21,8 +22,7 @@ import getFromUserSettings from '/imports/ui/services/users-settings'; import LayoutManagerContainer from '/imports/ui/components/layout/layout-manager/container'; import { withLayoutContext } from '/imports/ui/components/layout/context'; import VideoService from '/imports/ui/components/video-provider/service'; -import DebugWindow from '/imports/ui/components/debug-window/component' -import {Meteor} from "meteor/meteor"; +import DebugWindow from '/imports/ui/components/debug-window/component'; const CHAT_CONFIG = Meteor.settings.public.chat; const CHAT_ENABLED = CHAT_CONFIG.enabled; @@ -93,11 +93,10 @@ class Base extends Component { Session.set('isFullscreen', false); const users = Users.find({ - meetingId: Auth.meetingID, - validated: true, - userId: { $ne: localUserId }, - }, { fields: { name: 1, userId: 1 } } - ); + meetingId: Auth.meetingID, + validated: true, + userId: { $ne: localUserId }, + }, { fields: { name: 1, userId: 1 } }); users.observe({ added: (user) => { @@ -132,7 +131,7 @@ class Base extends Component { 'user', ); } - } + }, }); } @@ -267,7 +266,7 @@ class Base extends Component { const { meetingExisted } = this.state; return ( - <Fragment> + <> {meetingExist && Auth.loggedIn && <DebugWindow />} {meetingExist && Auth.loggedIn && <LayoutManagerContainer />} { @@ -275,7 +274,7 @@ class Base extends Component { ? <LoadingScreen /> : this.renderByState() } - </Fragment> + </> ); } } @@ -286,14 +285,11 @@ Base.defaultProps = defaultProps; const BaseContainer = withTracker(() => { const { animations, - userJoinAudioAlerts, - userJoinPushAlerts, } = Settings.application; const { credentials, loggedIn, - userID: localUserId, } = Auth; const { meetingId } = credentials; @@ -407,7 +403,8 @@ const BaseContainer = withTracker(() => { } else { Session.set('openPanel', ''); } - if ( Session.equals('subscriptionsReady', true )) { + window.dispatchEvent(new Event('panelChanged')); + if (Session.equals('subscriptionsReady', true)) { checkedUserSettings = true; } } diff --git a/bigbluebutton-html5/imports/ui/components/actions-bar/actions-dropdown/component.jsx b/bigbluebutton-html5/imports/ui/components/actions-bar/actions-dropdown/component.jsx index fdd6ff04cba778ce67e3ec53cdb28f4279e2e965..f4d41b5e688b28d150428f6e6fe68f15cf090ac5 100755 --- a/bigbluebutton-html5/imports/ui/components/actions-bar/actions-dropdown/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/actions-bar/actions-dropdown/component.jsx @@ -109,6 +109,11 @@ class ActionsDropdown extends PureComponent { } } + handleExternalVideoClick() { + const { mountModal } = this.props; + mountModal(<ExternalVideoModal />); + } + getAvailableActions() { const { intl, @@ -150,6 +155,7 @@ class ActionsDropdown extends PureComponent { } Session.set('openPanel', 'poll'); Session.set('forcePollOpen', true); + window.dispatchEvent(new Event('panelChanged')); }} /> ) @@ -223,17 +229,18 @@ class ActionsDropdown extends PureComponent { itemStyles[styles.presentationItem] = true; itemStyles[styles.isCurrent] = p.current; - return (<DropdownListItem - className={cx(itemStyles)} - icon="file" - iconRight={p.current ? 'check' : null} - label={p.name} - description="uploaded presentation file" - key={`uploaded-presentation-${p.id}`} - onClick={() => { - setPresentation(p.id, podId); - }} - /> + return ( + <DropdownListItem + className={cx(itemStyles)} + icon="file" + iconRight={p.current ? 'check' : null} + label={p.name} + description="uploaded presentation file" + key={`uploaded-presentation-${p.id}`} + onClick={() => { + setPresentation(p.id, podId); + }} + /> ); }); @@ -241,11 +248,6 @@ class ActionsDropdown extends PureComponent { return presentationItemElements; } - handleExternalVideoClick() { - const { mountModal } = this.props; - mountModal(<ExternalVideoModal />); - } - render() { const { intl, diff --git a/bigbluebutton-html5/imports/ui/components/captions/pad/component.jsx b/bigbluebutton-html5/imports/ui/components/captions/pad/component.jsx index a8af07de30a22ca4030ff5ec32b026faf111a93d..8f2c57e9f853859b14b0749c63342e977812ce44 100644 --- a/bigbluebutton-html5/imports/ui/components/captions/pad/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/captions/pad/component.jsx @@ -96,16 +96,6 @@ class Pad extends PureComponent { } } - toggleListen() { - const { - listening, - } = this.state; - - this.setState({ - listening: !listening, - }, this.handleListen); - } - handleListen() { const { locale, @@ -169,6 +159,16 @@ class Pad extends PureComponent { } } + toggleListen() { + const { + listening, + } = this.state; + + this.setState({ + listening: !listening, + }, this.handleListen); + } + render() { const { locale, @@ -182,6 +182,7 @@ class Pad extends PureComponent { if (!amIModerator) { Session.set('openPanel', 'userlist'); + window.dispatchEvent(new Event('panelChanged')); return null; } @@ -193,7 +194,10 @@ class Pad extends PureComponent { <header className={styles.header}> <div className={styles.title}> <Button - onClick={() => { Session.set('openPanel', 'userlist'); }} + onClick={() => { + Session.set('openPanel', 'userlist'); + window.dispatchEvent(new Event('panelChanged')); + }} aria-label={intl.formatMessage(intlMessages.hide)} label={name} icon="left_arrow" @@ -207,8 +211,7 @@ class Pad extends PureComponent { onClick={() => { this.toggleListen(); }} label={listening ? intl.formatMessage(intlMessages.dictationStop) - : intl.formatMessage(intlMessages.dictationStart) - } + : intl.formatMessage(intlMessages.dictationStart)} aria-describedby="dictationBtnDesc" color="primary" disabled={!this.recognition} @@ -216,12 +219,11 @@ class Pad extends PureComponent { <div id="dictationBtnDesc" hidden> {listening ? intl.formatMessage(intlMessages.dictationOffDesc) - : intl.formatMessage(intlMessages.dictationOnDesc) - } + : intl.formatMessage(intlMessages.dictationOnDesc)} </div> </span> - ) : null - } + ) + : null} {CaptionsService.canIOwnThisPad(ownerId) ? ( <Button @@ -231,8 +233,7 @@ class Pad extends PureComponent { aria-label={intl.formatMessage(intlMessages.takeOwnership)} label={intl.formatMessage(intlMessages.takeOwnership)} /> - ) : null - } + ) : null} </header> {listening ? ( <div> @@ -244,8 +245,7 @@ class Pad extends PureComponent { ref={(node) => { this.iterimResultContainer = node; }} /> </div> - ) : null - } + ) : null} <iframe title="etherpad" src={url} diff --git a/bigbluebutton-html5/imports/ui/components/captions/writer-menu/component.jsx b/bigbluebutton-html5/imports/ui/components/captions/writer-menu/component.jsx index 5874bf855bd91b9beacd4ffbb3fc6ed8b458dd0c..4b137ae284659c4c93b3b94a0d6afcc41cd6b99f 100644 --- a/bigbluebutton-html5/imports/ui/components/captions/writer-menu/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/captions/writer-menu/component.jsx @@ -58,7 +58,7 @@ class WriterMenu extends PureComponent { const { allLocales, intl } = this.props; const candidate = allLocales.filter( - l => l.locale.substring(0, 2) === intl.locale.substring(0, 2), + (l) => l.locale.substring(0, 2) === intl.locale.substring(0, 2), ); this.state = { @@ -80,6 +80,7 @@ class WriterMenu extends PureComponent { takeOwnership(locale); Session.set('captionsLocale', locale); Session.set('openPanel', 'captions'); + window.dispatchEvent(new Event('panelChanged')); closeModal(); } diff --git a/bigbluebutton-html5/imports/ui/components/note/component.jsx b/bigbluebutton-html5/imports/ui/components/note/component.jsx index 289f0d613025c71a5e4023ff7f7cb726a4723643..8a3baa898dcd3e7bbf73d258afa6e99847f99685 100644 --- a/bigbluebutton-html5/imports/ui/components/note/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/note/component.jsx @@ -63,6 +63,7 @@ class Note extends Component { <Button onClick={() => { Session.set('openPanel', 'userlist'); + window.dispatchEvent(new Event('panelChanged')); }} data-test="hideNoteLabel" aria-label={intl.formatMessage(intlMessages.hideNoteLabel)} diff --git a/bigbluebutton-html5/imports/ui/components/note/service.js b/bigbluebutton-html5/imports/ui/components/note/service.js index 9be3549bf7a578f2d7d385c8ba93fe184e178c13..369fbba7fd435ecfa09b78268acb468bcc4f5b0d 100644 --- a/bigbluebutton-html5/imports/ui/components/note/service.js +++ b/bigbluebutton-html5/imports/ui/components/note/service.js @@ -24,7 +24,7 @@ const getLang = () => { }; const getNoteParams = () => { - let config = {}; + const config = {}; const User = Users.findOne({ userId: Auth.userID }, { fields: { name: 1 } }); config.userName = User.name; config.lang = getLang(); @@ -67,6 +67,13 @@ const getRevs = () => { return note ? note.revs : 0; }; +const getLastRevs = () => { + const lastRevs = Session.get('noteLastRevs'); + + if (!lastRevs) return -1; + return lastRevs; +}; + const setLastRevs = (revs) => { const lastRevs = getLastRevs(); @@ -75,12 +82,7 @@ const setLastRevs = (revs) => { } }; -const getLastRevs = () => { - const lastRevs = Session.get('noteLastRevs'); - - if (!lastRevs) return -1; - return lastRevs; -}; +const isPanelOpened = () => Session.get('openPanel') === 'note'; const hasUnreadNotes = () => { const opened = isPanelOpened(); @@ -102,10 +104,9 @@ const toggleNotePanel = () => { 'openPanel', isPanelOpened() ? 'userlist' : 'note', ); + window.dispatchEvent(new Event('panelChanged')); }; -const isPanelOpened = () => Session.get('openPanel') === 'note'; - export default { getNoteURL, getReadOnlyURL, diff --git a/bigbluebutton-html5/imports/ui/components/poll/component.jsx b/bigbluebutton-html5/imports/ui/components/poll/component.jsx index 13f255aabe9445284108b67152dd1c8dc2dcf6cf..4fe38cf8c0d522c11abde6c74db1a1e5fc848eb4 100644 --- a/bigbluebutton-html5/imports/ui/components/poll/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/poll/component.jsx @@ -198,6 +198,7 @@ class Poll extends Component { if (!amIPresenter) { Session.set('openPanel', 'userlist'); Session.set('forcePollOpen', false); + window.dispatchEvent(new Event('panelChanged')); } } @@ -213,10 +214,6 @@ class Poll extends Component { }); } - handleInputChange(index, event) { - this.handleInputTextChange(index, event.target.value); - } - handleInputTextChange(index, text) { const { optList } = this.state; // This regex will replace any instance of 2 or more consecutive white spaces @@ -244,17 +241,38 @@ class Poll extends Component { this.setState({ question: validateInput(e.target.value), error: clearError ? null : error }); } + handlePollValuesText(text) { + if (text && text.length > 0) { + this.pushToCustomPollValues(text); + } + } + + handleRemoveOption(index) { + const { optList } = this.state; + const list = [...optList]; + list.splice(index, 1); + this.setState({ optList: list }); + } + + handleAddOption() { + const { optList } = this.state; + this.setState({ optList: [...optList, { val: '' }] }); + } + setOptListLength(len) { const { optList } = this.state; - len = len > MAX_CUSTOM_FIELDS ? MAX_CUSTOM_FIELDS : len; - let diff = len - optList.length; - if(diff > 0) { - while(diff--) { + let diff = len > MAX_CUSTOM_FIELDS + ? MAX_CUSTOM_FIELDS - optList.length + : len - optList.length; + if (diff > 0) { + while (diff > 0) { this.handleAddOption(); + diff -= 1; } } else { - while(diff++) { + while (diff < 0) { this.handleRemoveOption(); + diff += 1; } } } @@ -272,24 +290,6 @@ class Poll extends Component { } } - handlePollValuesText(text) { - if (text && text.length > 0) { - this.pushToCustomPollValues(text); - } - } - - handleRemoveOption(index) { - const { optList } = this.state; - const list = [...optList]; - list.splice(index, 1); - this.setState({ optList: list }); - } - - handleAddOption() { - const { optList } = this.state; - this.setState({ optList: [...optList, { val: '' }] }); - } - checkPollType() { const { type, optList } = this.state; let _type = type; @@ -299,19 +299,19 @@ class Poll extends Component { switch (_type) { case 'A-': - pollString = optList.map(x => x.val).sort().join(''); + pollString = optList.map((x) => x.val).sort().join(''); defaultMatch = pollString.match(/^(ABCDEFG)|(ABCDEF)|(ABCDE)|(ABCD)|(ABC)|(AB)$/gi); isDefault = defaultMatch && pollString.length === defaultMatch[0].length; _type = isDefault ? `${_type}${defaultMatch[0].length}` : 'custom'; break; case 'TF': - pollString = optList.map(x => x.val).join(''); + pollString = optList.map((x) => x.val).join(''); defaultMatch = pollString.match(/^(TRUEFALSE)|(FALSETRUE)$/gi); isDefault = defaultMatch && pollString.length === defaultMatch[0].length; if (!isDefault) _type = 'custom'; break; case 'YNA': - pollString = optList.map(x => x.val).join(''); + pollString = optList.map((x) => x.val).join(''); defaultMatch = pollString.match(/^(YesNoAbstention)$/gi); isDefault = defaultMatch && pollString.length === defaultMatch[0].length; if (!isDefault) _type = 'custom'; @@ -343,23 +343,25 @@ class Poll extends Component { placeholder={intl.formatMessage(intlMessages.customPlaceholder)} data-test="pollOptionItem" className={styles.pollOption} - onChange={e => this.handleInputChange(e, i)} + onChange={(e) => this.handleInputChange(e, i)} maxLength={MAX_INPUT_CHARS} /> - { i > 1 ? ( - <Button - className={styles.deleteBtn} - label={intl.formatMessage(intlMessages.delete)} - icon="delete" - data-test="deletePollOption" - hideLabel - circle - color="default" - onClick={() => { - this.handleRemoveOption(i); - }} - />) : <div style={{ width: '40px' }} /> - } + {i > 1 + ? ( + <Button + className={styles.deleteBtn} + label={intl.formatMessage(intlMessages.delete)} + icon="delete" + data-test="deletePollOption" + hideLabel + circle + color="default" + onClick={() => { + this.handleRemoveOption(i); + }} + /> + ) + : <div style={{ width: '40px' }} />} </div> {!hasVal && type !== 'RP' && error ? ( <div className={styles.inputError}>{error}</div> @@ -417,7 +419,7 @@ class Poll extends Component { data-test="pollQuestionArea" className={styles.pollQuestion} value={question} - onChange={e => this.handleTextareaChange(e)} + onChange={(e) => this.handleTextareaChange(e)} rows="4" cols="35" maxLength={QUESTION_MAX_INPUT_CHARS} @@ -486,34 +488,34 @@ class Poll extends Component { /> </div> { type - && ( - <div data-test="responseChoices"> - <h4>{intl.formatMessage(intlMessages.responseChoices)}</h4> - { - type === 'RP' - && ( - <div> - <span>{intl.formatMessage(intlMessages.typedResponseDesc)}</span> - <div className={styles.exampleResponse}> - <div className={styles.exampleTitle} /> - <div className={styles.responseInput}> - <div className={styles.rInput} /> - </div> + && ( + <div data-test="responseChoices"> + <h4>{intl.formatMessage(intlMessages.responseChoices)}</h4> + { + type === 'RP' + && ( + <div> + <span>{intl.formatMessage(intlMessages.typedResponseDesc)}</span> + <div className={styles.exampleResponse}> + <div className={styles.exampleTitle} /> + <div className={styles.responseInput}> + <div className={styles.rInput} /> </div> </div> - ) - } - { - (defaultPoll || type === 'RP') - && ( - <div style={{ - display: 'flex', - flexFlow: 'column', - }} - > - {defaultPoll && this.renderInputs()} - {defaultPoll - && ( + </div> + ) + } + { + (defaultPoll || type === 'RP') + && ( + <div style={{ + display: 'flex', + flexFlow: 'column', + }} + > + {defaultPoll && this.renderInputs()} + {defaultPoll + && ( <Button className={styles.addItemBtn} data-test="addItem" @@ -523,51 +525,49 @@ class Poll extends Component { disabled={optList.length === MAX_CUSTOM_FIELDS} onClick={() => this.handleAddOption()} /> - ) - } - <Button - className={styles.startPollBtn} - data-test="startPoll" - label={intl.formatMessage(intlMessages.startPollLabel)} - color="primary" - onClick={() => { - let hasVal = false; - optList.forEach((o) => { - if (o.val.length > 0) hasVal = true; - }); - - let err = null; - if (type === 'RP' && question.length === 0) err = intl.formatMessage(intlMessages.questionErr); - if (!hasVal && type !== 'RP') err = intl.formatMessage(intlMessages.optionErr); - if (err) return this.setState({ error: err }); - - this.setState({ isPolling: true }, () => { - const verifiedPollType = this.checkPollType(); - const verifiedOptions = optList.map((o) => { - if (o.val.length > 0) return o.val; - return null; - }); - if (verifiedPollType === 'custom') { - startCustomPoll( - verifiedPollType, - question, - _.compact(verifiedOptions), - ); - } else { - startPoll(verifiedPollType, question); - } + )} + <Button + className={styles.startPollBtn} + data-test="startPoll" + label={intl.formatMessage(intlMessages.startPollLabel)} + color="primary" + onClick={() => { + let hasVal = false; + optList.forEach((o) => { + if (o.val.length > 0) hasVal = true; + }); + + let err = null; + if (type === 'RP' && question.length === 0) err = intl.formatMessage(intlMessages.questionErr); + if (!hasVal && type !== 'RP') err = intl.formatMessage(intlMessages.optionErr); + if (err) return this.setState({ error: err }); + + return this.setState({ isPolling: true }, () => { + const verifiedPollType = this.checkPollType(); + const verifiedOptions = optList.map((o) => { + if (o.val.length > 0) return o.val; + return null; }); - }} - /> - { - FILE_DRAG_AND_DROP_ENABLED && type !== 'RP' && this.renderDragDrop() - } - </div> - ) + if (verifiedPollType === 'custom') { + startCustomPoll( + verifiedPollType, + question, + _.compact(verifiedOptions), + ); + } else { + startPoll(verifiedPollType, question); + } + }); + }} + /> + { + FILE_DRAG_AND_DROP_ENABLED && type !== 'RP' && this.renderDragDrop() + } + </div> + ) } - </div> - ) - } + </div> + )} </div> ); } @@ -603,7 +603,6 @@ class Poll extends Component { return this.renderPollOptions(); } - renderDragDrop() { const { intl } = this.props; return ( @@ -613,7 +612,7 @@ class Poll extends Component { </div> <DragAndDrop {...{ intl, MAX_INPUT_CHARS }} - handlePollValuesText={e => this.handlePollValuesText(e)} + handlePollValuesText={(e) => this.handlePollValuesText(e)} > <div className={styles.dragAndDropPollContainer} /> </DragAndDrop> @@ -621,7 +620,6 @@ class Poll extends Component { ); } - render() { const { intl, @@ -643,10 +641,10 @@ class Poll extends Component { icon="left_arrow" aria-label={intl.formatMessage(intlMessages.hidePollDesc)} className={styles.hideBtn} - onClick={() => { - Session.set('openPanel', 'userlist'); - window.dispatchEvent(new Event('panelChanged')); - }} + onClick={() => { + Session.set('openPanel', 'userlist'); + window.dispatchEvent(new Event('panelChanged')); + }} /> <Button label={intl.formatMessage(intlMessages.closeLabel)} diff --git a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-notes/component.jsx b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-notes/component.jsx index 80d36b0529c18b84aa7f93b682c5b129b0212449..24d9360efb4c0bb77745f01e9d188be2e4b2bf8d 100644 --- a/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-notes/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/user-list/user-list-content/user-notes/component.jsx @@ -50,7 +50,7 @@ class UserNotes extends Component { const lastRevs = NoteService.getLastRevs(); - if (revs !== 0 && revs > lastRevs) this.setState({ unread: true }); + if (revs !== 0 && revs > lastRevs) this.setUnread(true); } componentDidUpdate(prevProps) { @@ -58,14 +58,18 @@ class UserNotes extends Component { const { unread } = this.state; if (!isPanelOpened && !unread) { - if (prevProps.revs !== revs) this.setState({ unread: true }); + if (prevProps.revs !== revs) this.setUnread(true); } if (isPanelOpened && unread) { - this.setState({ unread: false }); + this.setUnread(false); } } + setUnread(unread) { + this.setState({ unread }); + } + renderNotes() { const { intl, disableNote } = this.props; const { unread } = this.state; @@ -92,6 +96,7 @@ class UserNotes extends Component { tabIndex={0} className={styles.listItem} onClick={NoteService.toggleNotePanel} + onKeyPress={() => { }} > <Icon iconName="copy" /> <div aria-hidden> @@ -104,8 +109,7 @@ class UserNotes extends Component { <Icon iconName="lock" /> <span id="lockedNote">{`${intl.formatMessage(intlMessages.locked)} ${intl.formatMessage(intlMessages.byModerator)}`}</span> </div> - ) : null - } + ) : null} </div> {notification} </div> @@ -113,7 +117,7 @@ class UserNotes extends Component { } render() { - const { intl, disableNote } = this.props; + const { intl } = this.props; if (!NoteService.isEnabled()) return null;