screensharing: remake error locales, split them into error classes

There are new locale strings for screensharing errors with a new format (Code X. #What to do) to make it easier for the end user
This commit is contained in:
prlanzarin 2021-01-12 12:24:01 -03:00
parent 25f5139973
commit 0a36da1e46
5 changed files with 109 additions and 115 deletions

View File

@ -31,7 +31,7 @@ const CLIENT_SIDE_ERRORS = {
};
const SERVER_SIDE_ERRORS = {
SFU_SERVER_SIDE_ERRORS,
...SFU_SERVER_SIDE_ERRORS,
}
const AGGREGATED_ERRORS = {

View File

@ -3,6 +3,7 @@ import logger from '/imports/startup/client/logger';
import BridgeService from './service';
import ScreenshareBroker from '/imports/ui/services/bbb-webrtc-sfu/screenshare-broker';
import { setSharingScreen } from '/imports/ui/components/screenshare/service';
import { SCREENSHARING_ERRORS } from './errors';
const SFU_CONFIG = Meteor.settings.public.kurento;
const SFU_URL = SFU_CONFIG.wsUrl;
@ -12,18 +13,23 @@ const SCREENSHARE_VIDEO_TAG = 'screenshareVideo';
const SEND_ROLE = 'send';
const RECV_ROLE = 'recv';
const errorCodeMap = {
1301: 1101,
1302: 1102,
1305: 1105,
1307: 1108, // This should be 1107, but I'm preserving the existing locales - prlanzarin
// the error-code mapping is bridge specific; that's why it's not in the errors util
const ERROR_MAP = {
1301: SCREENSHARING_ERRORS.SIGNALLING_TRANSPORT_DISCONNECTED,
1302: SCREENSHARING_ERRORS.SIGNALLING_TRANSPORT_CONNECTION_FAILED,
1305: SCREENSHARING_ERRORS.PEER_NEGOTIATION_FAILED,
1307: SCREENSHARING_ERRORS.ICE_STATE_FAILED,
}
const mapErrorCode = (error) => {
const { errorCode } = error;
const mappedErrorCode = errorCodeMap[errorCode];
if (errorCode == null || mappedErrorCode == null) return error;
error.errorCode = mappedErrorCode;
const mappedError = ERROR_MAP[errorCode];
if (errorCode == null || mappedError == null) return error;
error.errorCode = mappedError.errorCode;
error.errorMessage = mappedError.errorMessage;
error.message = mappedError.errorMessage;
return error;
}
@ -69,6 +75,7 @@ export default class KurentoScreenshareBridge {
logger.debug({
logCode: 'screenshare_reconnect_failed',
extraInfo: {
errorCode: error.errorCode,
errorMessage: error.errorMessage,
reconnecting: this.reconnecting,
role: this.role,
@ -101,6 +108,7 @@ export default class KurentoScreenshareBridge {
logger.debug({
logCode: 'screenshare_reconnect_failed',
extraInfo: {
errorCode: error.errorCode,
errorMessage: error.errorMessage,
reconnecting: this.reconnecting,
role: this.role,
@ -163,12 +171,12 @@ export default class KurentoScreenshareBridge {
handleBrokerFailure(error) {
mapErrorCode(error);
const { errorMessage, errorCause, errorCode } = error;
const { errorMessage, errorCode } = error;
logger.error({
logCode: 'screenshare_failure',
logCode: 'screenshare_broker_failure',
extraInfo: {
errorMessage, errorCode, errorCause,
errorCode, errorMessage,
role: this.broker.role,
started: this.broker.started,
reconnecting: this.reconnecting,
@ -232,12 +240,9 @@ export default class KurentoScreenshareBridge {
if (this.maxConnectionAttemptsReached()) {
this.clearReconnectionTimeout();
this.connectionAttempts = 0;
onFailure({
errorCode: 1120,
errorMessage: `MAX_CONNECTION_ATTEMPTS_REACHED`,
});
onFailure(SCREENSHARING_ERRORS.MEDIA_TIMEOUT);
return reject({ errorCode: 1120, errorMessage: "MAX_CONNECTION_ATTEMPTS_REACHED" });
return reject(SCREENSHARING_ERRORS.MEDIA_TIMEOUT);
}
};

View File

@ -2,12 +2,12 @@ import Meetings from '/imports/api/meetings';
import logger from '/imports/startup/client/logger';
import { fetchWebRTCMappedStunTurnServers, getMappedFallbackStun } from '/imports/utils/fetchStunTurnServers';
import loadAndPlayMediaStream from '/imports/ui/services/bbb-webrtc-sfu/load-play';
import { SCREENSHARING_ERRORS } from './errors';
const {
constraints: GDM_CONSTRAINTS,
mediaTimeouts: MEDIA_TIMEOUTS,
} = Meteor.settings.public.kurento.screenshare;
const {
baseTimeout: BASE_MEDIA_TIMEOUT,
maxTimeout: MAX_MEDIA_TIMEOUT,
@ -15,11 +15,15 @@ const {
timeoutIncreaseFactor: TIMEOUT_INCREASE_FACTOR,
} = MEDIA_TIMEOUTS;
const hasDisplayMedia = (typeof navigator.getDisplayMedia === 'function'
const HAS_DISPLAY_MEDIA = (typeof navigator.getDisplayMedia === 'function'
|| (navigator.mediaDevices && typeof navigator.mediaDevices.getDisplayMedia === 'function'));
const getConferenceBridge = () => Meetings.findOne().voiceProp.voiceConf;
const normalizeGetDisplayMediaError = (error) => {
return SCREENSHARING_ERRORS[error.name] || SCREENSHARING_ERRORS.GetDisplayMediaGenericError;
};
const getBoundGDM = () => {
if (typeof navigator.getDisplayMedia === 'function') {
return navigator.getDisplayMedia.bind(navigator);
@ -34,11 +38,7 @@ const getScreenStream = async () => {
// a promise rejection AND not generating a valid input screen stream, need to
// work around that manually for now - prlanzarin
if (stream == null) {
return Promise.reject({
errorMessage: 'NotSupportedError',
errorName: 'NotSupportedError',
errorCode: 9,
});
return Promise.reject(SCREENSHARING_ERRORS.NotSupportedError);
}
if (typeof stream.getVideoTracks === 'function'
@ -79,19 +79,16 @@ const getScreenStream = async () => {
return getDisplayMedia(GDM_CONSTRAINTS)
.then(gDMCallback)
.catch(error => {
const normalizedError = normalizeGetDisplayMediaError(error);
logger.error({
logCode: 'screenshare_getdisplaymedia_failed',
extraInfo: { errorName: error.name, errorCode: error.code },
extraInfo: { errorCode: normalizedError.errorCode, errorMessage: normalizedError.errorMessage },
}, 'getDisplayMedia call failed');
return Promise.reject({ errorCode: error.code, errorMessage: error.name || error.message });
return Promise.reject(normalizedError);
});
} else {
// getDisplayMedia isn't supported, error its way out
return Promise.reject({
errorMessage: 'NotSupportedError',
errorName: 'NotSupportedError',
errorCode: 9,
});
return Promise.reject(SCREENSHARING_ERRORS.NotSupportedError);
}
}
@ -135,17 +132,16 @@ const screenshareLoadAndPlayMediaStream = (stream, mediaElement, muted) => {
}, 'Screen share media play failed: autoplay error');
dispatchAutoplayHandlingEvent(mediaElement);
} else {
const normalizedError = {
errorCode: 1104,
errorMessage: error.message || 'SCREENSHARE_PLAY_FAILED',
throw {
errorCode: SCREENSHARING_ERRORS.SCREENSHARE_PLAY_FAILED.errorCode,
errorMessage: error.message || SCREENSHARING_ERRORS.SCREENSHARE_PLAY_FAILED.errorMessage,
};
throw normalizedError;
}
});
}
export default {
hasDisplayMedia,
HAS_DISPLAY_MEDIA,
getConferenceBridge,
getScreenStream,
getIceServers,

View File

@ -17,6 +17,14 @@ import {
screenShareEndAlert,
isVideoBroadcasting,
} from '/imports/ui/components/screenshare/service';
import { SCREENSHARING_ERRORS } from '/imports/api/screenshare/client/bridge/errors';
const BROWSER_RESULTS = browser();
const isMobileBrowser = (BROWSER_RESULTS ? BROWSER_RESULTS.mobile : false)
|| (BROWSER_RESULTS && BROWSER_RESULTS.os
? BROWSER_RESULTS.os.includes('Android') // mobile flag doesn't always work
: false);
const IS_SAFARI = BROWSER_RESULTS.name === 'safari';
const propTypes = {
intl: intlShape.isRequired,
@ -48,18 +56,6 @@ const intlMessages = defineMessages({
id: 'app.actionsBar.actionsDropdown.stopDesktopShareDesc',
description: 'adds context to stop desktop share option',
},
genericError: {
id: 'app.screenshare.genericError',
description: 'error message for when screensharing fails with unknown error',
},
NotAllowedError: {
id: 'app.screenshare.notAllowed',
description: 'error message when screen access was not granted',
},
NotSupportedError: {
id: 'app.screenshare.notSupportedError',
description: 'error message when trying to share screen in unsafe environments',
},
screenShareNotSupported: {
id: 'app.media.screenshare.notSupported',
descriptions: 'error message when trying share screen on unsupported browsers',
@ -68,63 +64,61 @@ const intlMessages = defineMessages({
id: 'app.media.screenshare.unavailable',
descriptions: 'title for unavailable screen share modal',
},
NotReadableError: {
id: 'app.screenshare.notReadableError',
description: 'error message when the browser failed to capture the screen',
finalError: {
id: 'app.screenshare.screenshareFinalError',
description: 'Screen sharing failures with no recovery procedure',
},
1108: {
id: 'app.deskshare.iceConnectionStateError',
description: 'Error message for ice connection state failure',
retryError: {
id: 'app.screenshare.screenshareRetryError',
description: 'Screen sharing failures where a retry is recommended',
},
1120: {
id: 'app.deskshare.mediaFlowTimeout',
description: 'Error message for screenshare media flow timeout',
retryOtherEnvError: {
id: 'app.screenshare.screenshareRetryOtherEnvError',
description: 'Screen sharing failures where a retry in another environment is recommended',
},
2000: {
id: 'app.sfu.mediaServerConnectionError2000',
description: 'Error message fired when the SFU cannot connect to the media server',
unsupportedEnvError: {
id: 'app.screenshare.screenshareUnsupportedEnv',
description: 'Screen sharing is not supported, changing browser or device is recommended',
},
2001: {
id: 'app.sfu.mediaServerOffline2001',
description: 'error message when SFU is offline',
},
2002: {
id: 'app.sfu.mediaServerNoResources2002',
description: 'Error message fired when the media server lacks disk, CPU or FDs',
},
2003: {
id: 'app.sfu.mediaServerRequestTimeout2003',
description: 'Error message fired when requests are timing out due to lack of resources',
},
2021: {
id: 'app.sfu.serverIceGatheringFailed2021',
description: 'Error message fired when the server cannot enact ICE gathering',
},
2022: {
id: 'app.sfu.serverIceStateFailed2022',
description: 'Error message fired when the server endpoint transitioned to a FAILED ICE state',
},
2200: {
id: 'app.sfu.mediaGenericError2200',
description: 'Error message fired when the SFU component generated a generic error',
},
2202: {
id: 'app.sfu.invalidSdp2202',
description: 'Error message fired when the clients provides an invalid SDP',
},
2203: {
id: 'app.sfu.noAvailableCodec2203',
description: 'Error message fired when the server has no available codec for the client',
permissionError: {
id: 'app.screenshare.screensharePermissionError',
description: 'Screen sharing failure due to lack of permission',
},
});
const BROWSER_RESULTS = browser();
const isMobileBrowser = (BROWSER_RESULTS ? BROWSER_RESULTS.mobile : false)
|| (BROWSER_RESULTS && BROWSER_RESULTS.os
? BROWSER_RESULTS.os.includes('Android') // mobile flag doesn't always work
: false);
const IS_SAFARI = BROWSER_RESULTS.name === 'safari';
const getErrorLocale = (errorCode) => {
switch (errorCode) {
// Denied getDisplayMedia permission error
case SCREENSHARING_ERRORS.NotAllowedError.errorCode:
return intlMessages.permissionError;
// Browser is supposed to be supported, but a browser-related error happening.
// Suggest retrying in another device/browser/env
case SCREENSHARING_ERRORS.AbortError.errorCode:
case SCREENSHARING_ERRORS.InvalidStateError.errorCode:
case SCREENSHARING_ERRORS.OverconstrainedError.errorCode:
case SCREENSHARING_ERRORS.TypeError.errorCode:
case SCREENSHARING_ERRORS.NotFoundError.errorCode:
case SCREENSHARING_ERRORS.NotReadableError.errorCode:
case SCREENSHARING_ERRORS.PEER_NEGOTIATION_FAILED.errorCode:
case SCREENSHARING_ERRORS.SCREENSHARE_PLAY_FAILED.errorCode:
case SCREENSHARING_ERRORS.MEDIA_NO_AVAILABLE_CODEC.errorCode:
case SCREENSHARING_ERRORS.MEDIA_INVALID_SDP.errorCode:
return intlMessages.retryOtherEnvError;
// Fatal errors where a retry isn't warranted. This probably means the server
// is misconfigured somehow or the provider is utterly botched, so nothing
// the end user can do besides requesting support
case SCREENSHARING_ERRORS.SIGNALLING_TRANSPORT_CONNECTION_FAILED.errorCode:
case SCREENSHARING_ERRORS.MEDIA_SERVER_CONNECTION_ERROR.errorCode:
case SCREENSHARING_ERRORS.SFU_INVALID_REQUEST.errorCode:
return intlMessages.finalError;
// Unsupported errors
case SCREENSHARING_ERRORS.NotSupportedError.errorCode:
return intlMessages.unsupportedEnvError;
// Fall through: everything else is an error which might be solved with a retry
default:
return intlMessages.retryError;
}
}
const ScreenshareButton = ({
intl,
@ -137,20 +131,20 @@ const ScreenshareButton = ({
}) => {
// This is the failure callback that will be passed to the /api/screenshare/kurento.js
// script on the presenter's call
const onFail = (error) => {
const { errorCode, errorMessage, errorReason } = error;
const errorLocale = errorCode || errorMessage || errorReason;
const handleFailure = (error) => {
const {
errorCode = SCREENSHARING_ERRORS.UNKNOWN_ERROR.errorCode,
errorMessage
} = error;
logger.error({
logCode: 'screenshare_failed',
extraInfo: { errorCode, errorMessage, errorReason },
extraInfo: { errorCode, errorMessage },
}, 'Screenshare failed');
const localizedError = intlMessages[error] || intlMessages.genericError;
notify(intl.formatMessage(localizedError), 'error', 'desktop');
const localizedError = getErrorLocale(errorCode);
notify(intl.formatMessage(localizedError, { 0: errorCode }), 'error', 'desktop');
screenshareHasEnded();
// Don't trigger the screen share end alert if presenter click to cancel on screen share dialog
if (error !== 'NotAllowedError') screenShareEndAlert();
};
const renderScreenshareUnavailableModal = () => {
@ -199,10 +193,10 @@ const ScreenshareButton = ({
onClick={isVideoBroadcasting
? screenshareHasEnded
: () => {
if (IS_SAFARI && !ScreenshareBridgeService.hasDisplayMedia) {
if (IS_SAFARI && !ScreenshareBridgeService.HAS_DISPLAY_MEDIA) {
renderScreenshareUnavailableModal();
} else {
shareScreen(onFail);
shareScreen(handleFailure);
}
}
}

View File

@ -121,10 +121,11 @@
"app.media.screenshare.notSupported": "Screensharing is not supported in this browser.",
"app.media.screenshare.autoplayBlockedDesc": "We need your permission to show you the presenter's screen.",
"app.media.screenshare.autoplayAllowLabel": "View shared screen",
"app.screenshare.notAllowed": "Error: Permission to access screen wasn't granted.",
"app.screenshare.notSupportedError": "Error: Screensharing is allowed only on safe (SSL) domains",
"app.screenshare.notReadableError": "Error: There was a failure while trying to capture your screen",
"app.screenshare.genericError": "Error: An error has occurred with screensharing, please try again",
"app.screenshare.screenshareFinalError": "Code {0}. Could not share the screen.",
"app.screenshare.screenshareRetryError": "Code {0}. Try sharing the screen again.",
"app.screenshare.screenshareRetryOtherEnvError": "Code {0}. Could not share the screen. Try again using a different browser or device.",
"app.screenshare.screenshareUnsupportedEnv": "Code {0}. Browser is not supported. Try again using a different browser or device.",
"app.screenshare.screensharePermissionError": "Code {0}. Permission to capture the screen needs to be granted.",
"app.meeting.ended": "This session has ended",
"app.meeting.meetingTimeRemaining": "Meeting time remaining: {0}",
"app.meeting.meetingTimeHasEnded": "Time ended. Meeting will close soon",
@ -592,8 +593,6 @@
"app.video.pagination.nextPage": "See next videos",
"app.video.clientDisconnected": "Webcam cannot be shared due to connection issues",
"app.fullscreenButton.label": "Make {0} fullscreen",
"app.deskshare.iceConnectionStateError": "Connection failed when sharing screen (ICE error 1108)",
"app.deskshare.mediaFlowTimeout": "Media could not reach the server (error 1120)",
"app.sfu.mediaServerConnectionError2000": "Unable to connect to media server (error 2000)",
"app.sfu.mediaServerOffline2001": "Media server is offline. Please try again later (error 2001)",
"app.sfu.mediaServerNoResources2002": "Media server has no available resources (error 2002)",