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:
parent
25f5139973
commit
0a36da1e46
@ -31,7 +31,7 @@ const CLIENT_SIDE_ERRORS = {
|
||||
};
|
||||
|
||||
const SERVER_SIDE_ERRORS = {
|
||||
SFU_SERVER_SIDE_ERRORS,
|
||||
...SFU_SERVER_SIDE_ERRORS,
|
||||
}
|
||||
|
||||
const AGGREGATED_ERRORS = {
|
||||
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)",
|
||||
|
Loading…
Reference in New Issue
Block a user