Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SCH-531]Implement polling fallback for waiting room if websocket fails #91

Merged
merged 2 commits into from
Oct 19, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions bugsnag.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,12 @@ const getBugsnagClient = () => {
return bugsnagClient;
};

export const notifyBugsnag = (error)=>{
if (window.bugsnag
&& window.bugsnag.notify
&& typeof window.bugsnag.notify === 'function') {
window.bugsnag.notify(error);
}
}

export { getBugsnagClient };
3 changes: 2 additions & 1 deletion lang/main.json
Original file line number Diff line number Diff line change
Expand Up @@ -599,7 +599,8 @@
"youMayTest": "You may test your audio and video while you wait.",
"callWillBegin": "Your call will automatically begin momentarily.",
"authenticationExpired": "Your appointment booking has expired",
"minutes": "{{duration}} Minutes"
"minutes": "{{duration}} Minutes",
"errorTitleKey": "Waiting area error."
},
"preCallMessage": {
"sitBack": "Sit back, relax and take a moment for yourself.",
Expand Down
125 changes: 95 additions & 30 deletions react/features/jane-waiting-area/components/SocketConnection.web.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,30 @@
// @flow
/* eslint-disable require-jsdoc,max-len,camelcase*/
/* eslint-disable */

import { Component } from 'react';

import { notifyBugsnag } from '../../../../bugsnag';
import { Socket } from '../../../../service/Websocket/socket';
import {
createWaitingAreaModalEvent,
createWaitingAreaParticipantStatusChangedEvent,
createWaitingAreaSocketEvent,
sendAnalytics
} from '../../analytics';
import {
redirectToStaticPage
} from '../../app/actions';
import { getLocalParticipantInfoFromJwt, getLocalParticipantType } from '../../base/participants/functions';
import { connect } from '../../base/redux';
import { playSound as playSoundAction } from '../../base/sounds';
import { sleep } from '../../base/util/helpers';
import { showErrorNotification as showErrorNotificationAction } from '../../notifications';
import {
setJaneWaitingAreaAuthState as setJaneWaitingAreaAuthStateAction,
updateRemoteParticipantsStatuses as updateRemoteParticipantsStatusesAction,
updateRemoteParticipantsStatusesFromSocket as updateRemoteParticipantsStatusesFromSocketAction
, joinConference as joinConferenceAction
} from '../actions';
import { POLL_INTERVAL, REDIRECT_TO_WELCOME_PAGE_DELAY, CLOSE_BROWSER_DELAY } from '../constants';
import {
checkRoomStatus,
getRemoteParticipantsStatuses, isRNSocketWebView,
Expand All @@ -38,18 +44,25 @@ type Props = {
updateRemoteParticipantsStatusesFromSocket: Function,
updateRemoteParticipantsStatuses: Function,
playSound: Function,
joinConference: Function,
setJaneWaitingAreaAuthState: Function,
remoteParticipantsStatuses: any
remoteParticipantsStatuses: any,
showErrorNotification: Function,
redirectToWelcomePage: Function
};

class SocketConnection extends Component<Props> {

socket: Object;

interval: ?IntervalID;

connectionAttempts: number;

constructor(props) {
super(props);
this.socket = null;
this.interval = undefined;
this.connectionAttempts = 0;
}

componentDidMount() {
Expand All @@ -70,14 +83,14 @@ class SocketConnection extends Component<Props> {
sendAnalytics(createWaitingAreaParticipantStatusChangedEvent('left'));

// sleep here to ensure the above code can be executed when the browser window is closed.
sleep(2000);
sleep(CLOSE_BROWSER_DELAY);
}
};

window.addEventListener('beforeunload', unloadHandler);
window.addEventListener('unload', unloadHandler);
}
this._connectSocket();
this._fetchDataAndconnectSocket();
}

componentDidUpdate() {
Expand All @@ -89,6 +102,10 @@ class SocketConnection extends Component<Props> {
}

componentWillUnmount() {
if (this.interval) {
clearInterval(this.interval);
this.interval = undefined;
}
this.socket && this.socket.disconnect();
}

Expand Down Expand Up @@ -117,41 +134,86 @@ class SocketConnection extends Component<Props> {
}

_connectionStatusListener(status) {
const { isRNWebViewPage, joinConference } = this.props;

if (isRNWebViewPage) {
sendMessageToIosApp({ message: status });
if (status && status.error && !this.interval) {
sendAnalytics(createWaitingAreaSocketEvent('error', status.error));
sendAnalytics(createWaitingAreaModalEvent('polling.started'));
notifyBugsnag(status.error);
console.log('socket fallback to polling');
this._polling();
}
if (status && status.error) {
joinConference();

if (status && status.event === 'connected' && this.interval) {
sendAnalytics(createWaitingAreaModalEvent('polling.stoped'));
clearInterval(this.interval);
this.interval = undefined
this.connectionAttempts = 0;
}
}

_redirectToWelcomePage() {
const { redirectToWelcomePage } = this.props;

// Wait 5 seconds before redirecting user to the welcome page
setTimeout(() => {
redirectToWelcomePage();
}, REDIRECT_TO_WELCOME_PAGE_DELAY);
}

_polling() {
this.interval
= setInterval(
() => {
this._fetchDataAndconnectSocket();
},
POLL_INTERVAL);
}

async _connectSocket() {
const { participantType, isRNWebViewPage, updateRemoteParticipantsStatuses, joinConference, setJaneWaitingAreaAuthState } = this.props;
async _fetchDataAndconnectSocket() {
const { participantType,
isRNWebViewPage,
updateRemoteParticipantsStatuses,
setJaneWaitingAreaAuthState,
showErrorNotification } = this.props;

try {
// fetch data
const response = await checkRoomStatus();
const remoteParticipantsStatuses = getRemoteParticipantsStatuses(response.participant_statuses, participantType);

// This action will update the remote participant states in reducer
updateRemoteParticipantsStatuses(remoteParticipantsStatuses);

this.socket = new Socket({
socket_host: response.socket_host,
ws_token: response.socket_token
});
this.socket.onMessageReceivedListener = this._onMessageReceivedListener.bind(this);
this.socket.connectionStatusListener = this._connectionStatusListener.bind(this);
this.socket.connect();
} catch (error) {
if (isRNWebViewPage) {
sendMessageToIosApp({ message: error });
} else if (error && error.error === 'Signature has expired') {
setJaneWaitingAreaAuthState('failed');
if (this.socket) {
this.socket.reconnect(response.socket_token);
} else {
joinConference();
this.socket = new Socket({
socket_host: response.socket_host,
ws_token: response.socket_token
});
this.socket.onMessageReceivedListener = this._onMessageReceivedListener.bind(this);
this.socket.connectionStatusListener = this._connectionStatusListener.bind(this);
this.socket.connect();
}
} catch (error) {
if (this.connectionAttempts === 3) {
if (isRNWebViewPage) {
sendMessageToIosApp({ message: error });
} else if (error && error.error === 'Signature has expired') {
setJaneWaitingAreaAuthState('failed');
}
showErrorNotification({
description: error && error.error,
titleKey: 'janeWaitingArea.errorTitleKey'
});

// send event to datadog
sendAnalytics(createWaitingAreaModalEvent('polling.stoped'));
clearInterval(this.interval);
this.interval = undefined
return this._redirectToWelcomePage();
}
this.connectionAttempts++;
this._fetchDataAndconnectSocket();
}
}

Expand Down Expand Up @@ -186,11 +248,14 @@ function mapDispatchToProps(dispatch) {
playSound(soundId) {
dispatch(playSoundAction(soundId));
},
joinConference() {
dispatch(joinConferenceAction());
},
setJaneWaitingAreaAuthState(state) {
dispatch(setJaneWaitingAreaAuthStateAction(state));
},
showErrorNotification(error) {
dispatch(showErrorNotificationAction(error));
},
redirectToWelcomePage() {
dispatch(redirectToStaticPage('/'));
}
};
}
Expand Down
11 changes: 8 additions & 3 deletions react/features/jane-waiting-area/components/modal/Modal.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,11 +111,16 @@ class Modal extends Component<Props> {
this._admitClient = this._admitClient.bind(this);
}

_joinConference() {
async _joinConference() {
const { joinConference } = this.props;

updateParticipantReadyStatus('joined');
joinConference();
try {
await updateParticipantReadyStatus('joined');
} catch (e) {
console.error(e);
} finally {
joinConference();
}
}

_admitClient() {
Expand Down
4 changes: 4 additions & 0 deletions react/features/jane-waiting-area/constants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/* eslint-disable */
export const POLL_INTERVAL = 5000;
export const REDIRECT_TO_WELCOME_PAGE_DELAY = 5000;
export const CLOSE_BROWSER_DELAY = 2000;
8 changes: 3 additions & 5 deletions react/features/jane-waiting-area/functions.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import jwtDecode from 'jwt-decode';
import _ from 'lodash';

import { notifyBugsnag } from '../../../bugsnag';
import {
createWaitingAreaParticipantStatusChangedEvent,
sendAnalytics
Expand All @@ -12,7 +13,6 @@ import {
getLocalParticipantType
} from '../base/participants/functions';
import { doGetJSON } from '../base/util';
import { showErrorNotification } from '../notifications';

import { UPDATE_REMOTE_PARTICIPANT_STATUSES } from './actionTypes';
import { updateRemoteParticipantsStatuses } from './actions';
Expand Down Expand Up @@ -135,6 +135,7 @@ export async function checkRoomStatus(): Promise<Object> {

return doGetJSON(url, true);
} catch (e) {
notifyBugsnag('Unable to retrieve the room state.');
throw Error(e);
}
}
Expand Down Expand Up @@ -185,11 +186,8 @@ export function updateParticipantReadyStatus(status: string): void {
})
.catch(error => {
sendAnalytics(createWaitingAreaParticipantStatusChangedEvent('failed'));
window.APP.store.dispatch(showErrorNotification({
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stop displaying this error notification due to a user complaint

descriptionKey: error,
titleKey: 'Waiting area error'
}));
console.error(error);
notifyBugsnag(error);
});
}

Expand Down
Loading