communication-react-19
Version:
React library for building modern communication user experiences utilizing Azure Communication Services (React 19 compatible fork)
118 lines • 5.95 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