diff --git a/bigbluebutton-html5/imports/startup/client/auth.js b/bigbluebutton-html5/imports/startup/client/auth.js index bc4c15923e7f84347e637bc2876760413e1d0c3d..b87b1c735cc03aebf52220c8d6e4d266b883f483 100644 --- a/bigbluebutton-html5/imports/startup/client/auth.js +++ b/bigbluebutton-html5/imports/startup/client/auth.js @@ -35,9 +35,6 @@ export function logoutRouteHandler(nextState, replace) { protocolPattern.test(logoutURL) ? logoutURL : `http://${logoutURL}`; - }) - .catch(() => { - replace({ pathname: '/error/500' }); }); } diff --git a/bigbluebutton-html5/imports/startup/client/base.jsx b/bigbluebutton-html5/imports/startup/client/base.jsx index 94c8f38fd074425a6306cf10fb09f1bf293ba131..e81a75c6421494d370155c5ca0a8c4401b577515 100644 --- a/bigbluebutton-html5/imports/startup/client/base.jsx +++ b/bigbluebutton-html5/imports/startup/client/base.jsx @@ -5,6 +5,7 @@ import PropTypes from 'prop-types'; import Auth from '/imports/ui/services/auth'; import AppContainer from '/imports/ui/components/app/container'; import ErrorScreen from '/imports/ui/components/error-screen/component'; +import MeetingEnded from '/imports/ui/components/meeting-ended/component'; import LoadingScreen from '/imports/ui/components/loading-screen/component'; import Settings from '/imports/ui/services/settings'; import IntlStartup from './intl'; @@ -14,12 +15,14 @@ const propTypes = { errorCode: PropTypes.number, subscriptionsReady: PropTypes.bool.isRequired, locale: PropTypes.string, + endedCode: PropTypes.string, }; const defaultProps = { error: undefined, errorCode: undefined, locale: undefined, + endedCode: undefined, }; class Base extends Component { @@ -54,6 +57,9 @@ class Base extends Component { const { loading, error } = this.state; const { subscriptionsReady, errorCode } = this.props; + const { endedCode } = this.props.params; + + if (endedCode) return (<MeetingEnded code={endedCode} />); if (error || errorCode) { return (<ErrorScreen code={errorCode}>{error}</ErrorScreen>); @@ -96,6 +102,7 @@ const BaseContainer = withRouter(withTracker(({ params, router }) => { const { credentials } = Auth; + const subscriptionErrorHandler = { onError: (error) => { console.error(error); @@ -106,6 +113,7 @@ const BaseContainer = withRouter(withTracker(({ params, router }) => { const subscriptionsHandlers = SUBSCRIPTIONS_NAME.map(name => Meteor.subscribe(name, credentials, subscriptionErrorHandler)); + return { locale: Settings.application.locale, subscriptionsReady: subscriptionsHandlers.every(handler => handler.ready()), diff --git a/bigbluebutton-html5/imports/startup/client/routes.js b/bigbluebutton-html5/imports/startup/client/routes.js index d7b43af13718c98b5600847f6906fdb7e7612f4d..f396812ae71284a456c183fe58a3dc5347165621 100644 --- a/bigbluebutton-html5/imports/startup/client/routes.js +++ b/bigbluebutton-html5/imports/startup/client/routes.js @@ -5,7 +5,7 @@ import { createHistory } from 'history'; import LoadingScreen from '/imports/ui/components/loading-screen/component'; import ChatContainer from '/imports/ui/components/chat/container'; import UserListContainer from '/imports/ui/components/user-list/container'; - +import MeetingEnded from '/imports/ui/components/meeting-ended/component'; import { joinRouteHandler, logoutRouteHandler, authenticatedRouteHandler } from './auth'; import Base from './base'; @@ -35,6 +35,7 @@ const renderRoutes = () => ( /> <Redirect from="users/chat" to="/users/chat/public" /> </Route> + <Route name="meeting-ended" path="/ended/:endedCode" component={Base} /> <Route name="error" path="/error/:errorCode" component={Base} /> <Redirect from="*" to="/error/404" /> </Router> diff --git a/bigbluebutton-html5/imports/ui/components/app/container.jsx b/bigbluebutton-html5/imports/ui/components/app/container.jsx index 05d1ef72c97b74cf553f50ccf155fa2eaedc468e..1c707f999501941eba183174880f5ffc8f221515 100644 --- a/bigbluebutton-html5/imports/ui/components/app/container.jsx +++ b/bigbluebutton-html5/imports/ui/components/app/container.jsx @@ -72,6 +72,7 @@ const AppContainer = (props) => { ); }; + export default withRouter(injectIntl(withModalMounter(withTracker(({ router, intl, baseControls }) => { const currentUser = Users.findOne({ userId: Auth.userID }); const isMeetingBreakout = meetingIsBreakout(); @@ -93,7 +94,7 @@ export default withRouter(injectIntl(withModalMounter(withTracker(({ router, int Users.find({ userId: Auth.userID }).observeChanges({ changed(id, fields) { if (fields.ejected) { - sendToError(403, intl.formatMessage(intlMessages.kickedMessage)); + router.push(`/ended/${403}`); } }, }); @@ -102,7 +103,7 @@ export default withRouter(injectIntl(withModalMounter(withTracker(({ router, int Meetings.find({ meetingId: Auth.meetingID }).observeChanges({ removed() { if (isMeetingBreakout) return; - sendToError(410, intl.formatMessage(intlMessages.endMeetingMessage)); + router.push(`/ended/${410}`); }, }); diff --git a/bigbluebutton-html5/imports/ui/components/error-screen/component.jsx b/bigbluebutton-html5/imports/ui/components/error-screen/component.jsx index 45a4295ee073f14430f071d4ab2665334371f6a5..38800a00934dc04bd6f76db922e529f26bd2eed3 100644 --- a/bigbluebutton-html5/imports/ui/components/error-screen/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/error-screen/component.jsx @@ -2,8 +2,8 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { defineMessages, injectIntl } from 'react-intl'; import Button from '/imports/ui/components/button/component'; - -import { styles } from './styles.scss'; +import { withRouter } from 'react-router'; +import styles from './styles.scss'; const intlMessages = defineMessages({ 500: { @@ -17,9 +17,6 @@ const intlMessages = defineMessages({ 401: { id: 'app.error.401', }, - 403: { - id: 'app.error.403', - }, leave: { id: 'app.error.leaveLabel', description: 'aria-label for leaving', @@ -38,15 +35,12 @@ const defaultProps = { }; class ErrorScreen extends Component { - - onClick() { - window.location = window.location.origin; - } - render() { - const { intl, code, children } = this.props; + const { + intl, code, children, router, + } = this.props; - let formatedMessage = intl.formatMessage(intlMessages[500]); + let formatedMessage = intl.formatMessage(intlMessages[defaultProps.code]); if (code in intlMessages) { formatedMessage = intl.formatMessage(intlMessages[code]); @@ -65,8 +59,8 @@ class ErrorScreen extends Component { </div> <div className={styles.content}> <Button - size={'sm'} - onClick={this.onClick} + size="sm" + onClick={() => router.push('/logout/')} label={intl.formatMessage(intlMessages.leave)} /> </div> @@ -75,7 +69,7 @@ class ErrorScreen extends Component { } } -export default injectIntl(ErrorScreen); +export default withRouter(injectIntl(ErrorScreen)); ErrorScreen.propTypes = propTypes; ErrorScreen.defaultProps = defaultProps; diff --git a/bigbluebutton-html5/imports/ui/components/meeting-ended/component.jsx b/bigbluebutton-html5/imports/ui/components/meeting-ended/component.jsx new file mode 100644 index 0000000000000000000000000000000000000000..fa7fbd679e5a7df71022a74faecbcd0c84783dc4 --- /dev/null +++ b/bigbluebutton-html5/imports/ui/components/meeting-ended/component.jsx @@ -0,0 +1,46 @@ +import React from 'react'; +import { withRouter } from 'react-router'; +import { defineMessages, injectIntl } from 'react-intl'; +import Button from '/imports/ui/components/button/component'; +import styles from './styles.scss'; + +const intlMessage = defineMessages({ + 410: { + id: 'app.meeting.ended', + description: 'message when meeting is ended', + }, + 403: { + id: 'app.error.kicked', + description: 'message when user is kicked', + }, + messageEnded: { + id: 'app.meeting.endedMessage', + description: 'message saying to go back to home screen', + }, + buttonOkay: { + id: 'app.meeting.endNotification.ok.label', + description: 'label okay for button', + }, +}); + +const MeetingEnded = ({ intl, router, code }) => ( + <div className={styles.parent}> + <div className={styles.modal}> + <div className={styles.content}> + <h1 className={styles.title}>{intl.formatMessage(intlMessage[code])}</h1> + <div className={styles.text}> + {intl.formatMessage(intlMessage.messageEnded)} + </div> + <Button + color="primary" + className={styles.button} + label={intl.formatMessage(intlMessage.buttonOkay)} + size="sm" + onClick={() => router.push('/logout')} + /> + </div> + </div> + </div> +); + +export default injectIntl(withRouter(MeetingEnded)); diff --git a/bigbluebutton-html5/imports/ui/components/meeting-ended/styles.scss b/bigbluebutton-html5/imports/ui/components/meeting-ended/styles.scss new file mode 100644 index 0000000000000000000000000000000000000000..38bceeca1976079ac425f30a28169227fb60467f --- /dev/null +++ b/bigbluebutton-html5/imports/ui/components/meeting-ended/styles.scss @@ -0,0 +1,45 @@ +@import "/imports/ui/stylesheets/variables/_all"; + +.parent { + height: 100%; + width: 100%; + display: flex; + justify-content: center; + align-items: center; +} + +.modal { + display: flex; + padding: $lg-padding-x; + background-color: $color-white; + flex-direction: column; + border-radius: $border-radius; + max-width: 95vw; + width: 600px; + +} + +.text { + color: $color-text; + font-weight: normal; + padding: $line-height-computed 0; + @include mq($small-only) { + font-size: $font-size-small; + }; +} + +.content { + text-align: center; +} + +.title { + margin: 0; + font-size: $font-size-large; + font-weight: $headings-font-weight; +} + +.button { + @include mq($small-only) { + font-size: $font-size-base; + } +} diff --git a/bigbluebutton-html5/imports/ui/services/auth/index.js b/bigbluebutton-html5/imports/ui/services/auth/index.js index 92fa54f5dd0b676874385ae4054f7bc6f1f8435c..782a6df3856210f165d208c779a913080fd5ac44 100644 --- a/bigbluebutton-html5/imports/ui/services/auth/index.js +++ b/bigbluebutton-html5/imports/ui/services/auth/index.js @@ -111,23 +111,7 @@ class Auth { } return new Promise((resolve) => { - const credentialsSnapshot = { - meetingId: this.meetingID, - requesterUserId: this.userID, - requesterToken: this.token, - }; - - // make sure users who did not connect are not added to the meeting - // do **not** use the custom call - it relies on expired data - Meteor.call('userLogout', credentialsSnapshot, (error) => { - if (error) { - log('error', error, { credentials: credentialsSnapshot }); - } else { - this.fetchLogoutUrl() - .then(this.clearCredentials) - .then(resolve); - } - }); + resolve(this._logoutURL); }); } @@ -179,10 +163,6 @@ class Auth { makeCall('validateAuthToken'); }); } - - fetchLogoutUrl() { - return Promise.resolve(this._logoutURL); - } } const AuthSingleton = new Auth(); diff --git a/bigbluebutton-html5/private/locales/en.json b/bigbluebutton-html5/private/locales/en.json index a77a5f301c3e9ce31b8a5bcda48e36fce4e842c9..568d7615ade261c9f11309b3fd434fc059b2aedc 100644 --- a/bigbluebutton-html5/private/locales/en.json +++ b/bigbluebutton-html5/private/locales/en.json @@ -40,6 +40,8 @@ "app.userList.menu.promoteUser.label": "Promote {0} to moderator", "app.userList.menu.demoteUser.label": "Demote {0} to viewer", "app.media.label": "Media", + "app.meeting.ended":"This session has ended", + "app.meeting.endedMessage":"You will be forwarded back to the home screen", "app.presentation.presentationToolbar.prevSlideLabel": "Previous slide", "app.presentation.presentationToolbar.prevSlideDesc": "Change the presentation to the previous slide", "app.presentation.presentationToolbar.nextSlideLabel": "Next slide", @@ -275,6 +277,7 @@ "app.toast.chat.plural":"you have {0} new messages in {1}", "app.notification.recordingStart": "This session is now being recorded", "app.notification.recordingStop": "This session is not being recorded anymore", + "app.meeting.endNotification.ok.label": "OK", "app.video.joinVideo": "Share webcam", "app.video.leaveVideo": "Unshare webcam" }