@microsoft/omnichannel-chat-widget 1.8.4-hotfix.system-message-spacing.1 → 1.8.4-main.05edfd3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/lib/cjs/common/Constants.js +3 -0
- package/lib/cjs/common/telemetry/TelemetryConstants.js +1 -0
- package/lib/cjs/common/telemetry/TelemetryManager.js +6 -1
- package/lib/cjs/common/telemetry/sanitizeSasInPayload.js +82 -0
- package/lib/cjs/common/utils.js +92 -16
- package/lib/cjs/components/citationpanestateful/CitationPaneStateful.js +8 -1
- package/lib/cjs/components/confirmationpanestateful/ConfirmationPaneStateful.js +12 -1
- package/lib/cjs/components/emailtranscriptpanestateful/EmailTranscriptPaneStateful.js +36 -13
- package/lib/cjs/components/livechatwidget/common/createMarkdown.js +98 -5
- package/lib/cjs/components/livechatwidget/common/endChat.js +6 -0
- package/lib/cjs/components/livechatwidget/common/initWebChatComposer.js +186 -1
- package/lib/cjs/components/livechatwidget/livechatwidgetstateful/LiveChatWidgetStateful.js +62 -3
- package/lib/cjs/components/postchatloadingpanestateful/PostChatLoadingPaneStateful.js +15 -3
- package/lib/cjs/components/postchatsurveypanestateful/PostChatSurveyPaneStateful.js +1 -0
- package/lib/cjs/components/prechatsurveypanestateful/PreChatSurveyPaneStateful.js +68 -2
- package/lib/cjs/components/webchatcontainerstateful/WebChatContainerStateful.js +116 -8
- package/lib/cjs/components/webchatcontainerstateful/common/DesignerChatAdapter.js +10 -0
- package/lib/cjs/components/webchatcontainerstateful/common/defaultProps/defaultMiddlewareLocalizedTexts.js +3 -0
- package/lib/cjs/components/webchatcontainerstateful/common/defaultProps/defaultWebChatContainerStatefulProps.js +2 -1
- package/lib/cjs/components/webchatcontainerstateful/common/defaultStyles/defaultAdaptiveCardStyles.js +3 -1
- package/lib/cjs/components/webchatcontainerstateful/common/defaultStyles/defaultWebChatStyles.js +1 -1
- package/lib/cjs/components/webchatcontainerstateful/common/utils/chatAdapterUtils.js +3 -1
- package/lib/cjs/components/webchatcontainerstateful/common/utils/citationA11y.js +195 -0
- package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/activityMiddleware.js +26 -7
- package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/attachmentMiddleware.js +2 -1
- package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/attachments/AdaptiveCardAccessibilityWrapper.js +203 -0
- package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/defaultStyles/defaultSystemMessageStyles.js +7 -2
- package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/defaultStyles/defaultTimestampRetryStyles.js +8 -1
- package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/timestamps/NotDeliveredTimestamp.js +3 -13
- package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/attachmentSentAnnouncementMiddleware.js +81 -0
- package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/attachmentUploadValidatorMiddleware.js +29 -1
- package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/localizedStringsBotInitialsMiddleware.js +52 -8
- package/lib/cjs/components/webchatcontainerstateful/webchatcontroller/notification/NotificationHandler.js +55 -1
- package/lib/cjs/contexts/common/LiveChatWidgetContextInitialState.js +11 -0
- package/lib/esm/common/Constants.js +3 -0
- package/lib/esm/common/telemetry/TelemetryConstants.js +1 -0
- package/lib/esm/common/telemetry/TelemetryManager.js +6 -1
- package/lib/esm/common/telemetry/sanitizeSasInPayload.js +75 -0
- package/lib/esm/common/utils.js +87 -13
- package/lib/esm/components/citationpanestateful/CitationPaneStateful.js +8 -1
- package/lib/esm/components/confirmationpanestateful/ConfirmationPaneStateful.js +12 -1
- package/lib/esm/components/emailtranscriptpanestateful/EmailTranscriptPaneStateful.js +37 -14
- package/lib/esm/components/livechatwidget/common/createMarkdown.js +98 -5
- package/lib/esm/components/livechatwidget/common/endChat.js +6 -0
- package/lib/esm/components/livechatwidget/common/initWebChatComposer.js +186 -1
- package/lib/esm/components/livechatwidget/livechatwidgetstateful/LiveChatWidgetStateful.js +63 -4
- package/lib/esm/components/postchatloadingpanestateful/PostChatLoadingPaneStateful.js +16 -4
- package/lib/esm/components/postchatsurveypanestateful/PostChatSurveyPaneStateful.js +1 -0
- package/lib/esm/components/prechatsurveypanestateful/PreChatSurveyPaneStateful.js +69 -3
- package/lib/esm/components/webchatcontainerstateful/WebChatContainerStateful.js +116 -8
- package/lib/esm/components/webchatcontainerstateful/common/DesignerChatAdapter.js +10 -0
- package/lib/esm/components/webchatcontainerstateful/common/defaultProps/defaultMiddlewareLocalizedTexts.js +3 -0
- package/lib/esm/components/webchatcontainerstateful/common/defaultProps/defaultWebChatContainerStatefulProps.js +2 -1
- package/lib/esm/components/webchatcontainerstateful/common/defaultStyles/defaultAdaptiveCardStyles.js +3 -1
- package/lib/esm/components/webchatcontainerstateful/common/defaultStyles/defaultWebChatStyles.js +1 -1
- package/lib/esm/components/webchatcontainerstateful/common/utils/chatAdapterUtils.js +3 -1
- package/lib/esm/components/webchatcontainerstateful/common/utils/citationA11y.js +188 -0
- package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/activityMiddleware.js +23 -5
- package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/attachmentMiddleware.js +2 -1
- package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/attachments/AdaptiveCardAccessibilityWrapper.js +195 -0
- package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/defaultStyles/defaultSystemMessageStyles.js +7 -2
- package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/defaultStyles/defaultTimestampRetryStyles.js +8 -1
- package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/timestamps/NotDeliveredTimestamp.js +3 -13
- package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/attachmentSentAnnouncementMiddleware.js +74 -0
- package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/attachmentUploadValidatorMiddleware.js +29 -1
- package/lib/esm/components/webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/localizedStringsBotInitialsMiddleware.js +52 -8
- package/lib/esm/components/webchatcontainerstateful/webchatcontroller/notification/NotificationHandler.js +55 -1
- package/lib/esm/contexts/common/LiveChatWidgetContextInitialState.js +11 -0
- package/lib/types/common/Constants.d.ts +2 -0
- package/lib/types/common/telemetry/TelemetryConstants.d.ts +1 -0
- package/lib/types/common/telemetry/sanitizeSasInPayload.d.ts +24 -0
- package/lib/types/common/utils.d.ts +3 -1
- package/lib/types/components/webchatcontainerstateful/common/utils/chatAdapterUtils.d.ts +1 -1
- package/lib/types/components/webchatcontainerstateful/common/utils/citationA11y.d.ts +1 -0
- package/lib/types/components/webchatcontainerstateful/interfaces/IAdaptiveCardStyles.d.ts +1 -0
- package/lib/types/components/webchatcontainerstateful/interfaces/IWebChatContainerStatefulProps.d.ts +7 -0
- package/lib/types/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/activityMiddleware.d.ts +2 -0
- package/lib/types/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/attachments/AdaptiveCardAccessibilityWrapper.d.ts +18 -0
- package/lib/types/components/webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/defaultStyles/defaultSystemMessageStyles.d.ts +3 -0
- package/lib/types/components/webchatcontainerstateful/webchatcontroller/middlewares/storemiddlewares/attachmentSentAnnouncementMiddleware.d.ts +12 -0
- package/lib/types/components/webchatcontainerstateful/webchatcontroller/notification/NotificationHandler.d.ts +7 -0
- package/lib/types/contexts/common/ILiveChatWidgetLocalizedTexts.d.ts +17 -0
- package/package.json +28 -9
package/README.md
CHANGED
|
@@ -311,6 +311,7 @@ BroadcastService.getMessageByEventName("CloseChat").subscribe(async (msg) => {
|
|
|
311
311
|
[Create LiveChatWidget with Webpack5 and TypeScript](https://github.com/microsoft/omnichannel-chat-widget/blob/main/docs/BuildingUsingWebpack5.md)\
|
|
312
312
|
[Omnichannel Features](https://github.com/microsoft/omnichannel-chat-widget/blob/main/docs/Features.md)\
|
|
313
313
|
[How to Add Visual Regression Tests](https://github.com/microsoft/omnichannel-chat-widget/blob/main/docs/VisualRegressionTestingGuide.md)\
|
|
314
|
+
[Accessibility Tooling](https://github.com/microsoft/omnichannel-chat-widget/blob/main/docs/accessibility/README.md)\
|
|
314
315
|
[Security](https://github.com/microsoft/omnichannel-chat-widget/blob/main/SECURITY.md)\
|
|
315
316
|
[Third Party Cookie Support](https://github.com/microsoft/omnichannel-chat-widget/blob/main/docs/Tpc.md)\
|
|
316
317
|
[Storybook](https://microsoft.github.io/omnichannel-chat-widget/docs/storybook/)
|
|
@@ -146,6 +146,9 @@ _defineProperty(Constants, "customEventValue", "customEventValue");
|
|
|
146
146
|
_defineProperty(Constants, "Hidden", "Hidden");
|
|
147
147
|
_defineProperty(Constants, "EndConversationDueToOverflow", "endconversationduetooverflow");
|
|
148
148
|
_defineProperty(Constants, "SkipSessionCloseForPersistentChatFlag", "skipSessionCloseForPersistentChat");
|
|
149
|
+
// Minimum font-size for input fields to prevent iOS Safari auto-zoom on focus
|
|
150
|
+
_defineProperty(Constants, "minInputFontSizePx", 16);
|
|
151
|
+
_defineProperty(Constants, "minInputFontSize", "16px");
|
|
149
152
|
const Regex = (_class = /*#__PURE__*/_createClass(function Regex() {
|
|
150
153
|
_classCallCheck(this, Regex);
|
|
151
154
|
}), _defineProperty(_class, "EmailRegex", "^(?:[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*|\"[^\"\\\\]*(?:\\\\.[^\"\\\\]*)*\")@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?)*|\\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\])$"), _class);
|
|
@@ -202,6 +202,7 @@ exports.TelemetryEvent = TelemetryEvent;
|
|
|
202
202
|
TelemetryEvent["QueueOverflowEvent"] = "QueueOverflowEvent";
|
|
203
203
|
TelemetryEvent["ProcessingHTMLTextMiddlewareFailed"] = "ProcessingHTMLTextMiddlewareFailed";
|
|
204
204
|
TelemetryEvent["ProcessingSanitizationMiddlewareFailed"] = "ProcessingSanitizationMiddlewareFailed";
|
|
205
|
+
TelemetryEvent["HTMLSanitized"] = "HTMLSanitized";
|
|
205
206
|
TelemetryEvent["FormatTagsMiddlewareJSONStringifyFailed"] = "FormatTagsMiddlewareJSONStringifyFailed";
|
|
206
207
|
TelemetryEvent["AttachmentUploadValidatorMiddlewareFailed"] = "AttachmentUploadValidatorMiddlewareFailed";
|
|
207
208
|
TelemetryEvent["CitationMiddlewareFailed"] = "CitationMiddlewareFailed";
|
|
@@ -11,6 +11,7 @@ var _appInsightsLogger = require("./loggers/appInsightsLogger");
|
|
|
11
11
|
var _ariaTelemetryLogger = require("./loggers/ariaTelemetryLogger");
|
|
12
12
|
var _consoleLogger = require("./loggers/consoleLogger");
|
|
13
13
|
var _defaultAriaConfig = require("./defaultConfigs/defaultAriaConfig");
|
|
14
|
+
var _sanitizeSasInPayload = require("./sanitizeSasInPayload");
|
|
14
15
|
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } }
|
|
15
16
|
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
|
|
16
17
|
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
|
@@ -81,11 +82,15 @@ const RegisterLoggers = () => {
|
|
|
81
82
|
};
|
|
82
83
|
};
|
|
83
84
|
const logTelemetry = telemetryEvent => {
|
|
85
|
+
// Redact Azure SAS credentials in any URL embedded in the payload (e.g.
|
|
86
|
+
// customer-supplied blob asset URLs in widget customization props) before
|
|
87
|
+
// it fans out to console, Aria, AppInsights, or any custom logger.
|
|
88
|
+
const sanitizedPayload = (0, _sanitizeSasInPayload.sanitizeSasInPayload)(telemetryEvent === null || telemetryEvent === void 0 ? void 0 : telemetryEvent.payload);
|
|
84
89
|
loggers.map(logger => {
|
|
85
90
|
var _payload, _telemetryInput$paylo;
|
|
86
91
|
const logLevel = telemetryEvent.logLevel ?? _TelemetryConstants.LogLevel.INFO;
|
|
87
92
|
const scenarioType = ((_payload = telemetryEvent.payload) === null || _payload === void 0 ? void 0 : _payload.scenarioType) ?? _TelemetryConstants.ScenarioType.UNDEFINED;
|
|
88
|
-
const telemetryInput = parseInput(
|
|
93
|
+
const telemetryInput = parseInput(sanitizedPayload, scenarioType);
|
|
89
94
|
telemetryInput.telemetryInfo = {
|
|
90
95
|
telemetryInfo: _TelemetryHelper.TelemetryHelper.buildTelemetryEvent(logLevel, telemetryInput)
|
|
91
96
|
};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.sanitizeSasInPayload = void 0;
|
|
7
|
+
/**
|
|
8
|
+
* Redacts the values of Azure Shared Access Signature (SAS) query parameters in any
|
|
9
|
+
* URL embedded in a telemetry payload, so customer-supplied SAS URLs (e.g. blob asset
|
|
10
|
+
* URLs in widget customization props) cannot leak credentials through Aria/AppInsights.
|
|
11
|
+
*
|
|
12
|
+
* Trigger: any URL whose query string contains `sig=`. When detected, every well-known
|
|
13
|
+
* SAS parameter value in that query is replaced with `[REDACTED]`. Other URLs and other
|
|
14
|
+
* substrings of the payload pass through unchanged.
|
|
15
|
+
*
|
|
16
|
+
* Coverage of SAS parameter names:
|
|
17
|
+
* - Service/Blob SAS: sig, sv, se, st, sp, spr, sr, sip
|
|
18
|
+
* - Account SAS: ss, srt
|
|
19
|
+
* - User-delegation key SAS: skoid, sktid, skt, ske, sks, skv, saoid, suoid, sce, sdd
|
|
20
|
+
* - Response header overrides: rscc, rscd, rsce, rscl, rsct
|
|
21
|
+
*
|
|
22
|
+
* Reference: https://learn.microsoft.com/en-us/rest/api/storageservices/create-service-sas
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
const SAS_PARAM_NAMES = new Set(["sig", "sv", "se", "st", "sp", "spr", "sr", "sip", "ss", "srt", "skoid", "sktid", "skt", "ske", "sks", "skv", "saoid", "suoid", "sce", "sdd", "rscc", "rscd", "rsce", "rscl", "rsct"]);
|
|
26
|
+
const REDACTED_VALUE = "[REDACTED]";
|
|
27
|
+
|
|
28
|
+
// Matches any http(s) URL substring up to whitespace / closing quote / angle-bracket / paren.
|
|
29
|
+
const URL_REGEX = /\bhttps?:\/\/[^\s"'<>)]+/gi;
|
|
30
|
+
const redactQueryIfSas = url => {
|
|
31
|
+
const queryIdx = url.indexOf("?");
|
|
32
|
+
if (queryIdx < 0) {
|
|
33
|
+
return url;
|
|
34
|
+
}
|
|
35
|
+
// Separate the fragment (#...) before processing the query — fragments come
|
|
36
|
+
// after the query string in URL syntax and must not be folded into the last
|
|
37
|
+
// query parameter's value when we split on `&`.
|
|
38
|
+
const afterQuestionMark = url.slice(queryIdx + 1);
|
|
39
|
+
const fragIdx = afterQuestionMark.indexOf("#");
|
|
40
|
+
const query = fragIdx < 0 ? afterQuestionMark : afterQuestionMark.slice(0, fragIdx);
|
|
41
|
+
const fragment = fragIdx < 0 ? "" : afterQuestionMark.slice(fragIdx); // includes the leading '#'
|
|
42
|
+
if (!/(^|&)sig=/i.test(query)) {
|
|
43
|
+
// No SAS signature present — leave the URL alone so legitimate non-SAS query
|
|
44
|
+
// strings (e.g. ?utm_source=...) are not mangled.
|
|
45
|
+
return url;
|
|
46
|
+
}
|
|
47
|
+
const base = url.slice(0, queryIdx);
|
|
48
|
+
const sanitizedQuery = query.split("&").map(pair => {
|
|
49
|
+
const eqIdx = pair.indexOf("=");
|
|
50
|
+
if (eqIdx < 0) {
|
|
51
|
+
return pair;
|
|
52
|
+
}
|
|
53
|
+
const name = pair.slice(0, eqIdx).toLowerCase();
|
|
54
|
+
return SAS_PARAM_NAMES.has(name) ? `${pair.slice(0, eqIdx)}=${REDACTED_VALUE}` : pair;
|
|
55
|
+
}).join("&");
|
|
56
|
+
return `${base}?${sanitizedQuery}${fragment}`;
|
|
57
|
+
};
|
|
58
|
+
const sanitizeString = s => s.replace(URL_REGEX, redactQueryIfSas);
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Deep-walk a value and redact SAS credentials in any string it contains.
|
|
62
|
+
* Returns a new object/array (originals are not mutated); non-string leaves are
|
|
63
|
+
* passed through. Tree-shaped, JSON-like values only — not designed for class
|
|
64
|
+
* instances, prototype-bound objects, or cyclic graphs.
|
|
65
|
+
*/
|
|
66
|
+
const sanitizeSasInPayload = value => {
|
|
67
|
+
if (typeof value === "string") {
|
|
68
|
+
return sanitizeString(value);
|
|
69
|
+
}
|
|
70
|
+
if (Array.isArray(value)) {
|
|
71
|
+
return value.map(sanitizeSasInPayload);
|
|
72
|
+
}
|
|
73
|
+
if (value !== null && typeof value === "object") {
|
|
74
|
+
const out = {};
|
|
75
|
+
for (const [k, v] of Object.entries(value)) {
|
|
76
|
+
out[k] = sanitizeSasInPayload(v);
|
|
77
|
+
}
|
|
78
|
+
return out;
|
|
79
|
+
}
|
|
80
|
+
return value;
|
|
81
|
+
};
|
|
82
|
+
exports.sanitizeSasInPayload = sanitizeSasInPayload;
|
package/lib/cjs/common/utils.js
CHANGED
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.getCustomEventValue = exports.getConversationDetailsCall = exports.getBroadcastChannelName = exports.formatTemplateString = exports.findParentFocusableElementsWithoutChildContainer = exports.findAllFocusableElement = exports.extractPreChatSurveyResponseValues = exports.escapeHtml = exports.debounceLeading = exports.createTimer = exports.createFileAndDownload = exports.checkContactIdError = exports.changeLanguageCodeFormatForWebChat = exports.addDelayInMs = void 0;
|
|
6
|
+
exports.getCustomEventValue = exports.getConversationDetailsCall = exports.getBroadcastChannelName = exports.formatTemplateString = exports.findParentFocusableElementsWithoutChildContainer = exports.findAllFocusableElement = exports.extractPreChatSurveyResponseValues = exports.escapeHtml = exports.debounceLeading = exports.createTimer = exports.createFileAndDownload = exports.checkContactIdError = exports.changeLanguageCodeFormatForWebChat = exports.announceMessageImmediately = exports.addDelayInMs = void 0;
|
|
7
7
|
exports.getDeviceType = getDeviceType;
|
|
8
8
|
exports.getWidgetEndChatEventName = exports.getWidgetCacheIdfromProps = exports.getWidgetCacheId = exports.getTimestampHourMinute = exports.getStateFromCache = exports.getLocaleDirection = exports.getIconText = exports.getDomain = void 0;
|
|
9
9
|
exports.isEndConversationDueToOverflowActivity = isEndConversationDueToOverflowActivity;
|
|
10
10
|
exports.parseAdaptiveCardPayload = exports.newGuid = exports.isValidCustomEvent = exports.isUndefinedOrEmpty = exports.isThisSessionPopout = exports.isNullOrUndefined = exports.isNullOrEmptyString = void 0;
|
|
11
11
|
exports.parseBooleanFromConfig = parseBooleanFromConfig;
|
|
12
|
-
exports.setTabIndices = exports.setOcUserAgent = exports.setFocusOnSendBox = exports.setFocusOnElement = exports.preventFocusToMoveOutOfElement = exports.parseLowerCaseString = void 0;
|
|
12
|
+
exports.setTabIndices = exports.setOcUserAgent = exports.setFocusOnSendBox = exports.setFocusOnElement = exports.setAriaHiddenForSiblings = exports.preventFocusToMoveOutOfElement = exports.parseLowerCaseString = void 0;
|
|
13
13
|
var _Constants = require("./Constants");
|
|
14
14
|
var _TelemetryConstants = require("./telemetry/TelemetryConstants");
|
|
15
15
|
var _omnichannelChatComponents = require("@microsoft/omnichannel-chat-components");
|
|
@@ -85,28 +85,69 @@ exports.findAllFocusableElement = findAllFocusableElement;
|
|
|
85
85
|
const preventFocusToMoveOutOfElement = elementId => {
|
|
86
86
|
const container = document.getElementById(elementId);
|
|
87
87
|
if (!container) {
|
|
88
|
-
return;
|
|
88
|
+
return () => {/* no-op */};
|
|
89
89
|
}
|
|
90
90
|
const focusableElements = findAllFocusableElement(container);
|
|
91
91
|
if (!focusableElements) {
|
|
92
|
-
return;
|
|
92
|
+
return () => {/* no-op */};
|
|
93
93
|
}
|
|
94
94
|
const firstFocusableElement = focusableElements[0];
|
|
95
95
|
const lastFocusableElement = focusableElements[focusableElements.length - 1];
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
}
|
|
107
|
-
|
|
96
|
+
const cleanups = [];
|
|
97
|
+
if (firstFocusableElement === lastFocusableElement) {
|
|
98
|
+
const handler = e => {
|
|
99
|
+
if (e.key === _KeyCodes.KeyCodes.TAB && !e.shiftKey) {
|
|
100
|
+
e.preventDefault();
|
|
101
|
+
firstFocusableElement.focus();
|
|
102
|
+
} else if (e.key === _KeyCodes.KeyCodes.TAB && e.shiftKey) {
|
|
103
|
+
e.preventDefault();
|
|
104
|
+
firstFocusableElement.focus();
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
firstFocusableElement.addEventListener("keydown", handler);
|
|
108
|
+
cleanups.push(() => firstFocusableElement.removeEventListener("keydown", handler));
|
|
109
|
+
} else {
|
|
110
|
+
const firstHandler = e => {
|
|
111
|
+
if (e.shiftKey && e.key === _KeyCodes.KeyCodes.TAB) {
|
|
112
|
+
e.preventDefault();
|
|
113
|
+
lastFocusableElement === null || lastFocusableElement === void 0 ? void 0 : lastFocusableElement.focus();
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
firstFocusableElement.addEventListener("keydown", firstHandler);
|
|
117
|
+
cleanups.push(() => firstFocusableElement.removeEventListener("keydown", firstHandler));
|
|
118
|
+
const lastHandler = e => {
|
|
119
|
+
if (!e.shiftKey && e.key === _KeyCodes.KeyCodes.TAB) {
|
|
120
|
+
e.preventDefault();
|
|
121
|
+
firstFocusableElement === null || firstFocusableElement === void 0 ? void 0 : firstFocusableElement.focus();
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
lastFocusableElement.addEventListener("keydown", lastHandler);
|
|
125
|
+
cleanups.push(() => lastFocusableElement.removeEventListener("keydown", lastHandler));
|
|
126
|
+
}
|
|
127
|
+
return () => cleanups.forEach(fn => fn());
|
|
108
128
|
};
|
|
109
129
|
exports.preventFocusToMoveOutOfElement = preventFocusToMoveOutOfElement;
|
|
130
|
+
const setAriaHiddenForSiblings = (elementId, shouldHide, stateMap) => {
|
|
131
|
+
const element = document.getElementById(elementId);
|
|
132
|
+
if (!(element !== null && element !== void 0 && element.parentElement)) return;
|
|
133
|
+
Array.from(element.parentElement.children).forEach(sibling => {
|
|
134
|
+
if (sibling !== element) {
|
|
135
|
+
if (shouldHide) {
|
|
136
|
+
stateMap.set(sibling, sibling.getAttribute("aria-hidden"));
|
|
137
|
+
sibling.setAttribute("aria-hidden", "true");
|
|
138
|
+
} else if (stateMap.has(sibling)) {
|
|
139
|
+
const original = stateMap.get(sibling);
|
|
140
|
+
if (original === null) {
|
|
141
|
+
sibling.removeAttribute("aria-hidden");
|
|
142
|
+
} else {
|
|
143
|
+
sibling.setAttribute("aria-hidden", original);
|
|
144
|
+
}
|
|
145
|
+
stateMap.delete(sibling);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
};
|
|
150
|
+
exports.setAriaHiddenForSiblings = setAriaHiddenForSiblings;
|
|
110
151
|
const setFocusOnSendBox = () => {
|
|
111
152
|
const sendBoxSelector = "textarea[data-id=\"webchat-sendbox-input\"]";
|
|
112
153
|
setFocusOnElement(sendBoxSelector);
|
|
@@ -117,6 +158,41 @@ const setFocusOnElement = selector => {
|
|
|
117
158
|
element === null || element === void 0 ? void 0 : element.focus();
|
|
118
159
|
};
|
|
119
160
|
exports.setFocusOnElement = setFocusOnElement;
|
|
161
|
+
const IMMEDIATE_ANNOUNCEMENT_REGION_ID = "oc-lcw-immediate-announcement";
|
|
162
|
+
|
|
163
|
+
// Announces a message to screen readers via an aria-live="assertive" region
|
|
164
|
+
// attached to document.body — outside the chat widget's DOM subtree — so the
|
|
165
|
+
// screen reader does not have to traverse chat content to reach it.
|
|
166
|
+
const announceMessageImmediately = message => {
|
|
167
|
+
if (!message || typeof document === "undefined") {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
let region = document.getElementById(IMMEDIATE_ANNOUNCEMENT_REGION_ID);
|
|
171
|
+
if (!region) {
|
|
172
|
+
region = document.createElement("div");
|
|
173
|
+
region.id = IMMEDIATE_ANNOUNCEMENT_REGION_ID;
|
|
174
|
+
region.setAttribute("aria-live", "assertive");
|
|
175
|
+
region.setAttribute("role", "alert");
|
|
176
|
+
region.setAttribute("aria-atomic", "true");
|
|
177
|
+
region.style.position = "absolute";
|
|
178
|
+
region.style.width = "1px";
|
|
179
|
+
region.style.height = "1px";
|
|
180
|
+
region.style.overflow = "hidden";
|
|
181
|
+
region.style.clip = "rect(0 0 0 0)";
|
|
182
|
+
region.style.clipPath = "inset(50%)";
|
|
183
|
+
region.style.whiteSpace = "nowrap";
|
|
184
|
+
document.body.appendChild(region);
|
|
185
|
+
}
|
|
186
|
+
region.textContent = "";
|
|
187
|
+
// Re-set on the next tick so screen readers detect the change even when
|
|
188
|
+
// the same message is announced twice in a row.
|
|
189
|
+
setTimeout(() => {
|
|
190
|
+
if (region) {
|
|
191
|
+
region.textContent = message;
|
|
192
|
+
}
|
|
193
|
+
}, 50);
|
|
194
|
+
};
|
|
195
|
+
exports.announceMessageImmediately = announceMessageImmediately;
|
|
120
196
|
const escapeHtml = inputString => {
|
|
121
197
|
const entityMap = {
|
|
122
198
|
"<": "<",
|
|
@@ -44,7 +44,7 @@ const CitationPaneStateful = props => {
|
|
|
44
44
|
|
|
45
45
|
// Initial focus pattern (mirrors ConfirmationPaneStateful): focus first focusable element (will re-attempt after visibility becomes true)
|
|
46
46
|
(0, _react.useEffect)(() => {
|
|
47
|
-
(0, _utils.preventFocusToMoveOutOfElement)(controlId);
|
|
47
|
+
const cleanup = (0, _utils.preventFocusToMoveOutOfElement)(controlId);
|
|
48
48
|
const focusableElements = (0, _utils.findAllFocusableElement)(`#${controlId}`);
|
|
49
49
|
requestAnimationFrame(() => {
|
|
50
50
|
if (focusableElements && focusableElements.length > 0 && focusableElements[0]) {
|
|
@@ -62,6 +62,13 @@ const CitationPaneStateful = props => {
|
|
|
62
62
|
Event: _TelemetryConstants.TelemetryEvent.UXCitationPaneCompleted,
|
|
63
63
|
ElapsedTimeInMilliseconds: uiTimer.milliSecondsElapsed
|
|
64
64
|
});
|
|
65
|
+
return () => {
|
|
66
|
+
// internal tracking: restore parent tab indices on unmount so focus
|
|
67
|
+
// can escape the widget even if the pane is dismissed by an
|
|
68
|
+
// external state change rather than a user button click.
|
|
69
|
+
(0, _utils.setTabIndices)(elements, initialTabIndexMap, true);
|
|
70
|
+
cleanup();
|
|
71
|
+
};
|
|
65
72
|
}, []);
|
|
66
73
|
|
|
67
74
|
// Retry focus once pane is actually visible (isReady) in case initial attempt occurred while wrapper was visibility:hidden
|
|
@@ -82,7 +82,7 @@ const ConfirmationPaneStateful = props => {
|
|
|
82
82
|
|
|
83
83
|
// Move focus to the first button
|
|
84
84
|
(0, _react.useEffect)(() => {
|
|
85
|
-
(0, _utils.preventFocusToMoveOutOfElement)(controlProps.id);
|
|
85
|
+
const cleanup = (0, _utils.preventFocusToMoveOutOfElement)(controlProps.id);
|
|
86
86
|
const focusableElements = (0, _utils.findAllFocusableElement)(`#${controlProps.id}`);
|
|
87
87
|
requestAnimationFrame(() => {
|
|
88
88
|
if (focusableElements && focusableElements.length > 0 && focusableElements[0]) {
|
|
@@ -100,6 +100,17 @@ const ConfirmationPaneStateful = props => {
|
|
|
100
100
|
Event: _TelemetryConstants.TelemetryEvent.UXConfirmationPaneCompleted,
|
|
101
101
|
ElapsedTimeInMilliseconds: uiTimer.milliSecondsElapsed
|
|
102
102
|
});
|
|
103
|
+
return () => {
|
|
104
|
+
// internal tracking: when the confirmation pane unmounts (page reload,
|
|
105
|
+
// parent state change, or any path that bypasses onConfirm /
|
|
106
|
+
// onCancel) the tab indices that were forced to -1 on sibling
|
|
107
|
+
// focusable elements must be restored, otherwise Tab order
|
|
108
|
+
// remains broken and focus can be trapped inside the widget.
|
|
109
|
+
// setTabIndices is a no-op when initialTabIndexMap is already
|
|
110
|
+
// empty (onConfirm / onCancel already restored).
|
|
111
|
+
(0, _utils.setTabIndices)(elements, initialTabIndexMap, true);
|
|
112
|
+
cleanup();
|
|
113
|
+
};
|
|
103
114
|
}, []);
|
|
104
115
|
return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_DimLayer.DimLayer, {
|
|
105
116
|
brightness: (controlProps === null || controlProps === void 0 ? void 0 : controlProps.brightnessValueOnDim) ?? "0.2"
|
|
@@ -35,16 +35,23 @@ const EmailTranscriptPaneStateful = props => {
|
|
|
35
35
|
const [state, dispatch] = (0, _useChatContextStore.default)();
|
|
36
36
|
const [facadeChatSDK] = (0, _useFacadeChatSDKStore.default)();
|
|
37
37
|
const [initialEmail, setInitialEmail] = (0, _react.useState)("");
|
|
38
|
-
|
|
38
|
+
// restoreFocus=false is used on the submit path: the notification banner
|
|
39
|
+
// (success or error) takes focus via NotificationHandler.setFocusOnNotificationCloseButton,
|
|
40
|
+
// so an intermediate restore to the chat-widget shell would otherwise cause SRs to
|
|
41
|
+
// announce "Enter <widget>" in between the dialog and the banner.
|
|
42
|
+
const closeEmailTranscriptPane = function () {
|
|
43
|
+
let restoreFocus = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
|
|
39
44
|
dispatch({
|
|
40
45
|
type: _LiveChatWidgetActionType.LiveChatWidgetActionType.SET_SHOW_EMAIL_TRANSCRIPT_PANE,
|
|
41
46
|
payload: false
|
|
42
47
|
});
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
+
if (restoreFocus) {
|
|
49
|
+
const previousFocusedElementId = state.appStates.previousElementIdOnFocusBeforeModalOpen;
|
|
50
|
+
if (previousFocusedElementId) {
|
|
51
|
+
(0, _utils.setFocusOnElement)("#" + previousFocusedElementId);
|
|
52
|
+
} else {
|
|
53
|
+
(0, _utils.setFocusOnSendBox)();
|
|
54
|
+
}
|
|
48
55
|
}
|
|
49
56
|
dispatch({
|
|
50
57
|
type: _LiveChatWidgetActionType.LiveChatWidgetActionType.SET_PREVIOUS_FOCUSED_ELEMENT_ID,
|
|
@@ -55,23 +62,31 @@ const EmailTranscriptPaneStateful = props => {
|
|
|
55
62
|
const onSend = (0, _react.useCallback)(async email => {
|
|
56
63
|
var _state$domainStates;
|
|
57
64
|
const liveChatContext = state === null || state === void 0 ? void 0 : (_state$domainStates = state.domainStates) === null || _state$domainStates === void 0 ? void 0 : _state$domainStates.liveChatContext;
|
|
58
|
-
closeEmailTranscriptPane();
|
|
65
|
+
closeEmailTranscriptPane(false);
|
|
59
66
|
const chatTranscriptBody = {
|
|
60
67
|
emailAddress: email,
|
|
61
68
|
attachmentMessage: (props === null || props === void 0 ? void 0 : props.attachmentMessage) ?? "The following attachment was uploaded during the conversation:"
|
|
62
69
|
};
|
|
63
70
|
try {
|
|
64
|
-
var _state$domainStates2, _state$domainStates2$;
|
|
71
|
+
var _state$domainStates2, _state$domainStates2$, _state$domainStates3, _state$domainStates3$;
|
|
65
72
|
await (facadeChatSDK === null || facadeChatSDK === void 0 ? void 0 : facadeChatSDK.emailLiveChatTranscript(chatTranscriptBody, {
|
|
66
73
|
liveChatContext
|
|
67
74
|
}));
|
|
68
|
-
|
|
75
|
+
const successMessage = (state === null || state === void 0 ? void 0 : (_state$domainStates2 = state.domainStates) === null || _state$domainStates2 === void 0 ? void 0 : (_state$domainStates2$ = _state$domainStates2.middlewareLocalizedTexts) === null || _state$domainStates2$ === void 0 ? void 0 : _state$domainStates2$.MIDDLEWARE_BANNER_FILE_EMAIL_ADDRESS_RECORDED_SUCCESS) ?? (_defaultMiddlewareLocalizedTexts.defaultMiddlewareLocalizedTexts === null || _defaultMiddlewareLocalizedTexts.defaultMiddlewareLocalizedTexts === void 0 ? void 0 : _defaultMiddlewareLocalizedTexts.defaultMiddlewareLocalizedTexts.MIDDLEWARE_BANNER_FILE_EMAIL_ADDRESS_RECORDED_SUCCESS);
|
|
76
|
+
// Announce from a live region at document.body level so the
|
|
77
|
+
// screen reader speaks the confirmation immediately, without
|
|
78
|
+
// traversing the chat transcript on the way to the banner.
|
|
79
|
+
// Prefix with the explicit state word so SR users hear the outcome
|
|
80
|
+
// (visual users already see a success icon on the banner).
|
|
81
|
+
const successSrPrefix = (state === null || state === void 0 ? void 0 : (_state$domainStates3 = state.domainStates) === null || _state$domainStates3 === void 0 ? void 0 : (_state$domainStates3$ = _state$domainStates3.middlewareLocalizedTexts) === null || _state$domainStates3$ === void 0 ? void 0 : _state$domainStates3$.MIDDLEWARE_SR_PREFIX_SUCCESS) ?? _defaultMiddlewareLocalizedTexts.defaultMiddlewareLocalizedTexts.MIDDLEWARE_SR_PREFIX_SUCCESS;
|
|
82
|
+
(0, _utils.announceMessageImmediately)(`${successSrPrefix}${successMessage}`);
|
|
83
|
+
_NotificationHandler.NotificationHandler.notifySuccess(_NotificationScenarios.NotificationScenarios.EmailAddressSaved, successMessage);
|
|
69
84
|
_TelemetryHelper.TelemetryHelper.logActionEventToAllTelemetry(_TelemetryConstants.LogLevel.INFO, {
|
|
70
85
|
Event: _TelemetryConstants.TelemetryEvent.EmailTranscriptSent,
|
|
71
86
|
Description: "Transcript sent to email successfully."
|
|
72
87
|
});
|
|
73
88
|
} catch (ex) {
|
|
74
|
-
var _state$
|
|
89
|
+
var _state$domainStates4, _state$domainStates4$, _state$domainStates5, _state$domainStates5$;
|
|
75
90
|
_TelemetryHelper.TelemetryHelper.logActionEventToAllTelemetry(_TelemetryConstants.LogLevel.ERROR, {
|
|
76
91
|
Event: _TelemetryConstants.TelemetryEvent.EmailTranscriptFailed,
|
|
77
92
|
Description: "Email transcript failed.",
|
|
@@ -79,8 +94,11 @@ const EmailTranscriptPaneStateful = props => {
|
|
|
79
94
|
exception: ex
|
|
80
95
|
}
|
|
81
96
|
});
|
|
82
|
-
const message = (0, _utils.formatTemplateString)((state === null || state === void 0 ? void 0 : (_state$
|
|
83
|
-
|
|
97
|
+
const message = (0, _utils.formatTemplateString)((state === null || state === void 0 ? void 0 : (_state$domainStates4 = state.domainStates) === null || _state$domainStates4 === void 0 ? void 0 : (_state$domainStates4$ = _state$domainStates4.middlewareLocalizedTexts) === null || _state$domainStates4$ === void 0 ? void 0 : _state$domainStates4$.MIDDLEWARE_BANNER_FILE_EMAIL_ADDRESS_RECORDED_ERROR) ?? _defaultMiddlewareLocalizedTexts.defaultMiddlewareLocalizedTexts.MIDDLEWARE_BANNER_FILE_EMAIL_ADDRESS_RECORDED_ERROR, [email]);
|
|
98
|
+
const bannerMessage = (props === null || props === void 0 ? void 0 : props.bannerMessageOnError) ?? message;
|
|
99
|
+
const errorSrPrefix = (state === null || state === void 0 ? void 0 : (_state$domainStates5 = state.domainStates) === null || _state$domainStates5 === void 0 ? void 0 : (_state$domainStates5$ = _state$domainStates5.middlewareLocalizedTexts) === null || _state$domainStates5$ === void 0 ? void 0 : _state$domainStates5$.MIDDLEWARE_SR_PREFIX_ERROR) ?? _defaultMiddlewareLocalizedTexts.defaultMiddlewareLocalizedTexts.MIDDLEWARE_SR_PREFIX_ERROR;
|
|
100
|
+
(0, _utils.announceMessageImmediately)(`${errorSrPrefix}${bannerMessage}`);
|
|
101
|
+
_NotificationHandler.NotificationHandler.notifyError(_NotificationScenarios.NotificationScenarios.EmailTranscriptError, bannerMessage);
|
|
84
102
|
}
|
|
85
103
|
}, [props.attachmentMessage, props.bannerMessageOnError, facadeChatSDK, state.domainStates.liveChatContext]);
|
|
86
104
|
const controlProps = {
|
|
@@ -103,7 +121,7 @@ const EmailTranscriptPaneStateful = props => {
|
|
|
103
121
|
|
|
104
122
|
// Move focus to the first button
|
|
105
123
|
(0, _react.useEffect)(() => {
|
|
106
|
-
(0, _utils.preventFocusToMoveOutOfElement)(controlProps.id);
|
|
124
|
+
const cleanup = (0, _utils.preventFocusToMoveOutOfElement)(controlProps.id);
|
|
107
125
|
const focusableElements = (0, _utils.findAllFocusableElement)(`#${controlProps.id}`);
|
|
108
126
|
if (focusableElements) {
|
|
109
127
|
focusableElements[0].focus();
|
|
@@ -118,6 +136,11 @@ const EmailTranscriptPaneStateful = props => {
|
|
|
118
136
|
Event: _TelemetryConstants.TelemetryEvent.UXEmailTranscriptPaneCompleted,
|
|
119
137
|
ElapsedTimeInMilliseconds: uiTimer.milliSecondsElapsed
|
|
120
138
|
});
|
|
139
|
+
return () => {
|
|
140
|
+
// internal tracking: restore parent tab indices on unmount.
|
|
141
|
+
(0, _utils.setTabIndices)(elements, initialTabIndexMap, true);
|
|
142
|
+
cleanup();
|
|
143
|
+
};
|
|
121
144
|
}, [initialEmail]);
|
|
122
145
|
return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_DimLayer.DimLayer, {
|
|
123
146
|
brightness: (controlProps === null || controlProps === void 0 ? void 0 : controlProps.brightnessValueOnDim) ?? "0.2"
|
|
@@ -42,10 +42,8 @@ const createMarkdown = (disableMarkdownMessageFormatting, disableNewLineMarkdown
|
|
|
42
42
|
|
|
43
43
|
markdown.disable(["strikethrough"]);
|
|
44
44
|
|
|
45
|
-
// Custom plugin to fix numbered list continuity
|
|
45
|
+
// Custom plugin to fix numbered list continuity and merge adjacent markdown links with the same href.
|
|
46
46
|
markdown.use(function (md) {
|
|
47
|
-
const originalRender = md.render.bind(md);
|
|
48
|
-
const originalRenderInline = md.renderInline.bind(md);
|
|
49
47
|
function preprocessText(text) {
|
|
50
48
|
// Handle numbered lists that come with double line breaks (knowledge article format)
|
|
51
49
|
// This ensures proper continuous numbering instead of separate lists
|
|
@@ -80,16 +78,111 @@ const createMarkdown = (disableMarkdownMessageFormatting, disableNewLineMarkdown
|
|
|
80
78
|
return result;
|
|
81
79
|
}
|
|
82
80
|
|
|
81
|
+
// Accessibility fix: when a bot emits content like "[1.](url) [View details](url)",
|
|
82
|
+
// markdown-it renders two sibling links that screen readers announce as two separate
|
|
83
|
+
// focusable links. Merge consecutive markdown link tokens with identical attributes so
|
|
84
|
+
// the number and label form one combined focusable link without discarding metadata.
|
|
85
|
+
md.core.ruler.after("inline", "merge_adjacent_same_href_links", function (state) {
|
|
86
|
+
const sortedAttrs = token => (token.attrs || []).map(attr => `${attr[0]}=${attr[1]}`).sort();
|
|
87
|
+
const hasSameAttributes = (first, second) => {
|
|
88
|
+
const firstAttrs = sortedAttrs(first);
|
|
89
|
+
const secondAttrs = sortedAttrs(second);
|
|
90
|
+
return firstAttrs.length === secondAttrs.length && firstAttrs.every((attr, index) => attr === secondAttrs[index]);
|
|
91
|
+
};
|
|
92
|
+
const collectLink = (children, index) => {
|
|
93
|
+
var _open$attrGet;
|
|
94
|
+
const open = children[index];
|
|
95
|
+
if (!open || open.type !== "link_open") {
|
|
96
|
+
return undefined;
|
|
97
|
+
}
|
|
98
|
+
const href = (_open$attrGet = open.attrGet) === null || _open$attrGet === void 0 ? void 0 : _open$attrGet.call(open, "href");
|
|
99
|
+
if (!href) {
|
|
100
|
+
return undefined;
|
|
101
|
+
}
|
|
102
|
+
let depth = 0;
|
|
103
|
+
for (let currentIndex = index; currentIndex < children.length; currentIndex++) {
|
|
104
|
+
const token = children[currentIndex];
|
|
105
|
+
if (token.type === "link_open") {
|
|
106
|
+
depth++;
|
|
107
|
+
} else if (token.type === "link_close") {
|
|
108
|
+
depth--;
|
|
109
|
+
if (depth === 0) {
|
|
110
|
+
return {
|
|
111
|
+
closeIndex: currentIndex,
|
|
112
|
+
href,
|
|
113
|
+
open
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return undefined;
|
|
119
|
+
};
|
|
120
|
+
const getNextAdjacentLink = (children, startIndex) => {
|
|
121
|
+
const separatorTokens = [];
|
|
122
|
+
let index = startIndex;
|
|
123
|
+
while (index < children.length && children[index].type === "text" && /^\s*$/.test(children[index].content || "")) {
|
|
124
|
+
separatorTokens.push(children[index]);
|
|
125
|
+
index++;
|
|
126
|
+
}
|
|
127
|
+
const link = collectLink(children, index);
|
|
128
|
+
if (!link) {
|
|
129
|
+
return undefined;
|
|
130
|
+
}
|
|
131
|
+
return {
|
|
132
|
+
...link,
|
|
133
|
+
openIndex: index,
|
|
134
|
+
separatorTokens
|
|
135
|
+
};
|
|
136
|
+
};
|
|
137
|
+
state.tokens.forEach(blockToken => {
|
|
138
|
+
if (blockToken.type !== "inline" || !blockToken.children) {
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
const mergedChildren = [];
|
|
142
|
+
const children = blockToken.children;
|
|
143
|
+
let index = 0;
|
|
144
|
+
while (index < children.length) {
|
|
145
|
+
const firstLink = collectLink(children, index);
|
|
146
|
+
if (!firstLink) {
|
|
147
|
+
mergedChildren.push(children[index]);
|
|
148
|
+
index++;
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
const linkTokens = [firstLink.open, ...children.slice(index + 1, firstLink.closeIndex)];
|
|
152
|
+
let nextIndex = firstLink.closeIndex + 1;
|
|
153
|
+
let mergedAnyLink = false;
|
|
154
|
+
let nextLink = getNextAdjacentLink(children, nextIndex);
|
|
155
|
+
while (nextLink && nextLink.href === firstLink.href && hasSameAttributes(firstLink.open, nextLink.open)) {
|
|
156
|
+
linkTokens.push(...nextLink.separatorTokens);
|
|
157
|
+
linkTokens.push(...children.slice(nextLink.openIndex + 1, nextLink.closeIndex));
|
|
158
|
+
nextIndex = nextLink.closeIndex + 1;
|
|
159
|
+
mergedAnyLink = true;
|
|
160
|
+
nextLink = getNextAdjacentLink(children, nextIndex);
|
|
161
|
+
}
|
|
162
|
+
if (mergedAnyLink) {
|
|
163
|
+
mergedChildren.push(...linkTokens, children[firstLink.closeIndex]);
|
|
164
|
+
index = nextIndex;
|
|
165
|
+
} else {
|
|
166
|
+
mergedChildren.push(children[index]);
|
|
167
|
+
index++;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
blockToken.children = mergedChildren;
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
|
|
83
174
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
84
175
|
md.render = function (text, env) {
|
|
176
|
+
const safeEnv = env ?? {};
|
|
85
177
|
const processedText = preprocessText(text);
|
|
86
|
-
return
|
|
178
|
+
return md.renderer.render(md.parse(processedText, safeEnv), md.options, safeEnv);
|
|
87
179
|
};
|
|
88
180
|
|
|
89
181
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
90
182
|
md.renderInline = function (text, env) {
|
|
183
|
+
const safeEnv = env ?? {};
|
|
91
184
|
const processedText = preprocessText(text);
|
|
92
|
-
return
|
|
185
|
+
return md.renderer.render(md.parseInline(processedText, safeEnv), md.options, safeEnv);
|
|
93
186
|
};
|
|
94
187
|
});
|
|
95
188
|
|
|
@@ -11,6 +11,7 @@ var _renderSurveyHelpers = require("./renderSurveyHelpers");
|
|
|
11
11
|
var _omnichannelChatComponents = require("@microsoft/omnichannel-chat-components");
|
|
12
12
|
var _ConversationState = require("../../../contexts/common/ConversationState");
|
|
13
13
|
var _LazyLoadActivity = require("../../webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/activities/LazyLoadActivity");
|
|
14
|
+
var _activityMiddleware = require("../../webchatcontainerstateful/webchatcontroller/middlewares/renderingmiddlewares/activityMiddleware");
|
|
14
15
|
var _LiveChatWidgetActionType = require("../../../contexts/common/LiveChatWidgetActionType");
|
|
15
16
|
var _NotificationHandler = require("../../webchatcontainerstateful/webchatcontroller/notification/NotificationHandler");
|
|
16
17
|
var _NotificationScenarios = require("../../webchatcontainerstateful/webchatcontroller/enums/NotificationScenarios");
|
|
@@ -249,6 +250,7 @@ const endChat = async (props, facadeChatSDK, state, dispatch, setAdapter, setWeb
|
|
|
249
250
|
|
|
250
251
|
// Call direct reset to ensure LazyLoadHandler gets reset regardless of broadcast timing
|
|
251
252
|
_LazyLoadActivity.LazyLoadHandler.directReset();
|
|
253
|
+
(0, _activityMiddleware.resetActivityMiddlewareCache)();
|
|
252
254
|
_omnichannelChatComponents.BroadcastService.postMessage({
|
|
253
255
|
eventName: _TelemetryConstants.BroadcastEvent.PersistentConversationReset
|
|
254
256
|
});
|
|
@@ -344,6 +346,10 @@ const closeChatStateCleanUp = dispatch => {
|
|
|
344
346
|
type: _LiveChatWidgetActionType.LiveChatWidgetActionType.SET_CITATIONS,
|
|
345
347
|
payload: {}
|
|
346
348
|
});
|
|
349
|
+
dispatch({
|
|
350
|
+
type: _LiveChatWidgetActionType.LiveChatWidgetActionType.SET_SHOW_EMAIL_TRANSCRIPT_PANE,
|
|
351
|
+
payload: false
|
|
352
|
+
});
|
|
347
353
|
|
|
348
354
|
// Dismiss the chat disconnect notification banner if it was shown
|
|
349
355
|
_NotificationHandler.NotificationHandler.dismissNotification(_NotificationScenarios.NotificationScenarios.ChatDisconnect);
|