@azure/communication-react
Version:
React library for building modern communication user experiences utilizing Azure Communication Services
118 lines • 6 kB
JavaScript
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import { getLatestErrors } from './baseSelectors';
import { createSelector } from 'reselect';
/**
* Select the first fiew active errors from the state for the {@link ErrorBar} component.
*
* In case there are many errors, only a few top errors are returned to avoid
* filling up the UI with too many errors.
*
* Invariants:
* - {@link ErrorType} is never repeated in the returned errors.
* - Errors are returned in a fixed order by {@link ErrorType}.
*
* @public
*/
export const errorBarSelector = createSelector([getLatestErrors], (latestErrors) => {
var _a;
// The order in which the errors are returned is significant: The `ErrorBar` shows errors on the UI in that order.
// There are several options for the ordering:
// - Sorted by when the errors happened (latest first / oldest first).
// - Stable sort by error type.
//
// We chose to stable sort by error type: We intend to show only a small number of errors on the UI and we do not
// have timestamps for errors.
const activeErrorMessages = [];
let specificSendMessageErrorSeen = false;
{
const error = latestUnableToReachChatServiceError(latestErrors);
if (error !== undefined) {
activeErrorMessages.push(error);
}
}
{
const error = latestAccessDeniedError(latestErrors);
if (error !== undefined) {
activeErrorMessages.push(error);
}
}
const sendMessageError = latestErrors['ChatThreadClient.sendMessage'];
{
const error = latestNotInThisThreadError(latestErrors);
if (error !== undefined) {
if (sendMessageError !== undefined) {
activeErrorMessages.push({
type: 'sendMessageNotInChatThread',
// Set the latest timestamp of all the errors that translated to an active error.
timestamp: sendMessageError.timestamp > ((_a = error.timestamp) !== null && _a !== void 0 ? _a : 0) ? sendMessageError.timestamp : error.timestamp
});
specificSendMessageErrorSeen = true;
}
else {
activeErrorMessages.push(error);
}
}
}
if (!specificSendMessageErrorSeen && sendMessageError !== undefined) {
activeErrorMessages.push({
type: 'sendMessageGeneric',
timestamp: sendMessageError.timestamp
});
}
// We only return the first few errors to avoid filling up the UI with too many `MessageBar`s.
activeErrorMessages.splice(maxErrorCount);
return {
activeErrorMessages: activeErrorMessages
};
});
const maxErrorCount = 3;
const accessErrorTargets = ['ChatThreadClient.getProperties', 'ChatThreadClient.listMessages', 'ChatThreadClient.listParticipants', 'ChatThreadClient.sendMessage', 'ChatThreadClient.sendTypingNotification'];
const latestUnableToReachChatServiceError = (latestErrors) => {
return latestActiveErrorSatisfying(latestErrors, 'unableToReachChatService', (error) => {
return !!error && !!error.innerError && 'code' in error.innerError && error.innerError.code === 'REQUEST_SEND_ERROR';
});
};
const latestAccessDeniedError = (latestErrors) => {
return latestActiveErrorSatisfying(latestErrors, 'accessDenied', (error) => {
return !!error && !!error.innerError && 'statusCode' in error.innerError && error.innerError.statusCode === 401;
});
};
const latestNotInThisThreadError = (latestErrors) => {
return latestActiveErrorSatisfying(latestErrors, 'userNotInChatThread', (error) => {
if (!error || !error.innerError) {
return false;
}
// Explicitly ignore 400 REST error when listParticipants() is called and a BotContact MRI is found in the participants.
// This check should be removed when the chat SDK has handled this issue. Note: The this does not stop the error being logged to the console.
// To the best of our ability we have confirmed this to have no impact on the participantList returned (all valid participants are still returned), nor
// does it have an impact on the participant list updating on other participants joining/leaving or on individual participant actions like removeParticipant.
if (isErrorDueToBotContact(error)) {
return false;
}
// Chat service returns 403 if a user has been removed from a thread.
// Chat service returns either 400 or 404 if the thread ID is malformed, depending on how the thread ID is malformed.
return [400, 403, 404].some(statusCode => 'statusCode' in error.innerError && error.innerError.statusCode === statusCode);
});
};
const botContactMRIPrefix = '28:';
const isErrorDueToBotContact = (error) => 'statusCode' in error.innerError && error.innerError.statusCode === 400 && error.innerError.message.includes(`Identifier format is not supported (${botContactMRIPrefix}`);
const latestActiveErrorSatisfying = (errors, activeErrorType, predicate) => {
const activeErrorMessages = [];
for (const target of accessErrorTargets) {
const error = errors[target];
if (predicate(error)) {
activeErrorMessages.push({
type: activeErrorType,
timestamp: error.timestamp
});
}
}
if (activeErrorMessages.length === 0) {
return undefined;
}
// We're actually sure that both timestamps will always exist, because we set them above.
activeErrorMessages.sort((a, b) => { var _a, _b, _c, _d; return ((_b = (_a = a.timestamp) === null || _a === void 0 ? void 0 : _a.getTime()) !== null && _b !== void 0 ? _b : 0) - ((_d = (_c = b.timestamp) === null || _c === void 0 ? void 0 : _c.getTime()) !== null && _d !== void 0 ? _d : 0); });
return activeErrorMessages[activeErrorMessages.length - 1];
};
//# sourceMappingURL=errorBarSelector.js.map