@azure/communication-react 1.2.3-alpha-202205110014.0 → 1.2.3-alpha-202205140015.0

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.
Files changed (122) hide show
  1. package/dist/communication-react.d.ts +16 -5
  2. package/dist/dist-cjs/communication-react/index.js +400 -272
  3. package/dist/dist-cjs/communication-react/index.js.map +1 -1
  4. package/dist/dist-esm/acs-ui-common/src/telemetryVersion.js +1 -1
  5. package/dist/dist-esm/chat-component-bindings/src/handlers/createHandlers.d.ts +4 -1
  6. package/dist/dist-esm/chat-component-bindings/src/handlers/createHandlers.d.ts.map +1 -1
  7. package/dist/dist-esm/chat-component-bindings/src/handlers/createHandlers.js +4 -2
  8. package/dist/dist-esm/chat-component-bindings/src/handlers/createHandlers.js.map +1 -1
  9. package/dist/dist-esm/communication-react/src/index.d.ts +2 -2
  10. package/dist/dist-esm/communication-react/src/index.d.ts.map +1 -1
  11. package/dist/dist-esm/communication-react/src/index.js +1 -1
  12. package/dist/dist-esm/communication-react/src/index.js.map +1 -1
  13. package/dist/dist-esm/react-components/src/components/ChatMessage/ChatMessageComponent.d.ts +4 -2
  14. package/dist/dist-esm/react-components/src/components/ChatMessage/ChatMessageComponent.d.ts.map +1 -1
  15. package/dist/dist-esm/react-components/src/components/ChatMessage/ChatMessageComponent.js +2 -2
  16. package/dist/dist-esm/react-components/src/components/ChatMessage/ChatMessageComponent.js.map +1 -1
  17. package/dist/dist-esm/react-components/src/components/ChatMessage/ChatMessageComponentAsEditBox.d.ts +4 -1
  18. package/dist/dist-esm/react-components/src/components/ChatMessage/ChatMessageComponentAsEditBox.d.ts.map +1 -1
  19. package/dist/dist-esm/react-components/src/components/ChatMessage/ChatMessageComponentAsEditBox.js +24 -15
  20. package/dist/dist-esm/react-components/src/components/ChatMessage/ChatMessageComponentAsEditBox.js.map +1 -1
  21. package/dist/dist-esm/react-components/src/components/ComplianceBanner/ComplianceBanner.d.ts +41 -0
  22. package/dist/dist-esm/react-components/src/components/ComplianceBanner/ComplianceBanner.d.ts.map +1 -0
  23. package/dist/dist-esm/react-components/src/components/ComplianceBanner/ComplianceBanner.js +196 -0
  24. package/dist/dist-esm/react-components/src/components/ComplianceBanner/ComplianceBanner.js.map +1 -0
  25. package/dist/dist-esm/react-components/src/components/ComplianceBanner/Utils.d.ts +20 -0
  26. package/dist/dist-esm/react-components/src/components/ComplianceBanner/Utils.d.ts.map +1 -0
  27. package/dist/dist-esm/react-components/src/components/ComplianceBanner/Utils.js +42 -0
  28. package/dist/dist-esm/react-components/src/components/ComplianceBanner/Utils.js.map +1 -0
  29. package/dist/dist-esm/react-components/src/components/ComplianceBanner/index.d.ts +3 -0
  30. package/dist/dist-esm/react-components/src/components/ComplianceBanner/index.d.ts.map +1 -0
  31. package/dist/dist-esm/react-components/src/components/ComplianceBanner/index.js +4 -0
  32. package/dist/dist-esm/react-components/src/components/ComplianceBanner/index.js.map +1 -0
  33. package/dist/dist-esm/react-components/src/components/Dialpad/Dialpad.d.ts +50 -0
  34. package/dist/dist-esm/react-components/src/components/Dialpad/Dialpad.d.ts.map +1 -0
  35. package/dist/dist-esm/react-components/src/components/Dialpad/Dialpad.js +72 -0
  36. package/dist/dist-esm/react-components/src/components/Dialpad/Dialpad.js.map +1 -0
  37. package/dist/dist-esm/react-components/src/components/InputBoxComponent.d.ts.map +1 -1
  38. package/dist/dist-esm/react-components/src/components/InputBoxComponent.js +1 -2
  39. package/dist/dist-esm/react-components/src/components/InputBoxComponent.js.map +1 -1
  40. package/dist/dist-esm/react-components/src/components/LocalVideoTile.d.ts +29 -0
  41. package/dist/dist-esm/react-components/src/components/LocalVideoTile.d.ts.map +1 -0
  42. package/dist/dist-esm/react-components/src/components/LocalVideoTile.js +66 -0
  43. package/dist/dist-esm/react-components/src/components/LocalVideoTile.js.map +1 -0
  44. package/dist/dist-esm/react-components/src/components/MessageThread.d.ts +10 -4
  45. package/dist/dist-esm/react-components/src/components/MessageThread.d.ts.map +1 -1
  46. package/dist/dist-esm/react-components/src/components/MessageThread.js.map +1 -1
  47. package/dist/dist-esm/react-components/src/components/RemoteVideoTile.d.ts +2 -0
  48. package/dist/dist-esm/react-components/src/components/RemoteVideoTile.d.ts.map +1 -1
  49. package/dist/dist-esm/react-components/src/components/RemoteVideoTile.js +2 -0
  50. package/dist/dist-esm/react-components/src/components/RemoteVideoTile.js.map +1 -1
  51. package/dist/dist-esm/react-components/src/components/SendBox.d.ts.map +1 -1
  52. package/dist/dist-esm/react-components/src/components/SendBox.js +0 -2
  53. package/dist/dist-esm/react-components/src/components/SendBox.js.map +1 -1
  54. package/dist/dist-esm/react-components/src/components/VideoGallery.d.ts.map +1 -1
  55. package/dist/dist-esm/react-components/src/components/VideoGallery.js +18 -40
  56. package/dist/dist-esm/react-components/src/components/VideoGallery.js.map +1 -1
  57. package/dist/dist-esm/react-components/src/components/index.d.ts +5 -1
  58. package/dist/dist-esm/react-components/src/components/index.d.ts.map +1 -1
  59. package/dist/dist-esm/react-components/src/components/index.js +2 -0
  60. package/dist/dist-esm/react-components/src/components/index.js.map +1 -1
  61. package/dist/dist-esm/react-components/src/components/styles/ChatMessageComponent.styles.d.ts.map +1 -1
  62. package/dist/dist-esm/react-components/src/components/styles/ChatMessageComponent.styles.js +1 -2
  63. package/dist/dist-esm/react-components/src/components/styles/ChatMessageComponent.styles.js.map +1 -1
  64. package/dist/dist-esm/react-components/src/components/styles/Dialpad.styles.d.ts +22 -0
  65. package/dist/dist-esm/react-components/src/components/styles/Dialpad.styles.d.ts.map +1 -0
  66. package/dist/dist-esm/react-components/src/components/styles/Dialpad.styles.js +74 -0
  67. package/dist/dist-esm/react-components/src/components/styles/Dialpad.styles.js.map +1 -0
  68. package/dist/dist-esm/react-components/src/components/styles/InputBoxComponent.style.d.ts +1 -1
  69. package/dist/dist-esm/react-components/src/components/styles/InputBoxComponent.style.d.ts.map +1 -1
  70. package/dist/dist-esm/react-components/src/components/styles/InputBoxComponent.style.js +20 -25
  71. package/dist/dist-esm/react-components/src/components/styles/InputBoxComponent.style.js.map +1 -1
  72. package/dist/dist-esm/react-components/src/components/styles/SendBox.styles.d.ts +0 -1
  73. package/dist/dist-esm/react-components/src/components/styles/SendBox.styles.d.ts.map +1 -1
  74. package/dist/dist-esm/react-components/src/components/styles/SendBox.styles.js +3 -3
  75. package/dist/dist-esm/react-components/src/components/styles/SendBox.styles.js.map +1 -1
  76. package/dist/dist-esm/react-components/src/components/utils/formatPhoneNumber.d.ts +5 -0
  77. package/dist/dist-esm/react-components/src/components/utils/formatPhoneNumber.d.ts.map +1 -0
  78. package/dist/dist-esm/react-components/src/components/utils/formatPhoneNumber.js +44 -0
  79. package/dist/dist-esm/react-components/src/components/utils/formatPhoneNumber.js.map +1 -0
  80. package/dist/dist-esm/react-components/src/localization/LocalizationProvider.d.ts.map +1 -1
  81. package/dist/dist-esm/react-components/src/localization/LocalizationProvider.js.map +1 -1
  82. package/dist/dist-esm/react-components/src/theming/themes.d.ts.map +1 -1
  83. package/dist/dist-esm/react-components/src/theming/themes.js +6 -0
  84. package/dist/dist-esm/react-components/src/theming/themes.js.map +1 -1
  85. package/dist/dist-esm/react-composites/src/composites/CallComposite/components/CallArrangement.d.ts +2 -3
  86. package/dist/dist-esm/react-composites/src/composites/CallComposite/components/CallArrangement.d.ts.map +1 -1
  87. package/dist/dist-esm/react-composites/src/composites/CallComposite/components/CallArrangement.js +2 -3
  88. package/dist/dist-esm/react-composites/src/composites/CallComposite/components/CallArrangement.js.map +1 -1
  89. package/dist/dist-esm/react-composites/src/composites/CallComposite/pages/CallPage.d.ts.map +1 -1
  90. package/dist/dist-esm/react-composites/src/composites/CallComposite/pages/CallPage.js +3 -1
  91. package/dist/dist-esm/react-composites/src/composites/CallComposite/pages/CallPage.js.map +1 -1
  92. package/dist/dist-esm/react-composites/src/composites/CallComposite/pages/LobbyPage.js +1 -1
  93. package/dist/dist-esm/react-composites/src/composites/CallComposite/pages/LobbyPage.js.map +1 -1
  94. package/dist/dist-esm/react-composites/src/composites/CallComposite/utils/Utils.d.ts +0 -19
  95. package/dist/dist-esm/react-composites/src/composites/CallComposite/utils/Utils.d.ts.map +1 -1
  96. package/dist/dist-esm/react-composites/src/composites/CallComposite/utils/Utils.js +0 -39
  97. package/dist/dist-esm/react-composites/src/composites/CallComposite/utils/Utils.js.map +1 -1
  98. package/dist/dist-esm/react-composites/src/composites/CallWithChatComposite/CallWithChatComposite.js +1 -1
  99. package/dist/dist-esm/react-composites/src/composites/CallWithChatComposite/CallWithChatComposite.js.map +1 -1
  100. package/dist/dist-esm/react-composites/src/composites/ChatComposite/adapter/AzureCommunicationChatAdapter.d.ts +3 -1
  101. package/dist/dist-esm/react-composites/src/composites/ChatComposite/adapter/AzureCommunicationChatAdapter.d.ts.map +1 -1
  102. package/dist/dist-esm/react-composites/src/composites/ChatComposite/adapter/AzureCommunicationChatAdapter.js +4 -2
  103. package/dist/dist-esm/react-composites/src/composites/ChatComposite/adapter/AzureCommunicationChatAdapter.js.map +1 -1
  104. package/dist/dist-esm/react-composites/src/composites/ChatComposite/adapter/ChatAdapter.d.ts +4 -1
  105. package/dist/dist-esm/react-composites/src/composites/ChatComposite/adapter/ChatAdapter.d.ts.map +1 -1
  106. package/dist/dist-esm/react-composites/src/composites/localization/index.d.ts +7 -0
  107. package/dist/dist-esm/react-composites/src/composites/localization/index.d.ts.map +1 -1
  108. package/dist/dist-esm/react-composites/src/composites/localization/index.js +8 -0
  109. package/dist/dist-esm/react-composites/src/composites/localization/index.js.map +1 -1
  110. package/dist/dist-esm/react-composites/src/index-public.d.ts +2 -0
  111. package/dist/dist-esm/react-composites/src/index-public.d.ts.map +1 -0
  112. package/dist/dist-esm/react-composites/src/index-public.js +8 -0
  113. package/dist/dist-esm/react-composites/src/index-public.js.map +1 -0
  114. package/package.json +7 -7
  115. package/dist/dist-esm/react-composites/src/composites/CallComposite/components/ComplianceBanner.d.ts +0 -13
  116. package/dist/dist-esm/react-composites/src/composites/CallComposite/components/ComplianceBanner.d.ts.map +0 -1
  117. package/dist/dist-esm/react-composites/src/composites/CallComposite/components/ComplianceBanner.js +0 -127
  118. package/dist/dist-esm/react-composites/src/composites/CallComposite/components/ComplianceBanner.js.map +0 -1
  119. package/dist/dist-esm/react-composites/src/index.d.ts +0 -2
  120. package/dist/dist-esm/react-composites/src/index.d.ts.map +0 -1
  121. package/dist/dist-esm/react-composites/src/index.js +0 -4
  122. package/dist/dist-esm/react-composites/src/index.js.map +0 -1
@@ -191,7 +191,7 @@ const fromFlatCommunicationIdentifier = (id) => {
191
191
  // Copyright (c) Microsoft Corporation.
192
192
  // Licensed under the MIT license.
193
193
  // GENERATED FILE. DO NOT EDIT MANUALLY.
194
- var telemetryVersion = '1.2.3-alpha-202205110014.0';
194
+ var telemetryVersion = '1.2.3-alpha-202205140015.0';
195
195
 
196
196
  // Copyright (c) Microsoft Corporation.
197
197
  /**
@@ -1539,9 +1539,9 @@ const defaultSendBoxActiveBorderThicknessREM = 0.125;
1539
1539
  * @private
1540
1540
  */
1541
1541
  const borderAndBoxShadowStyle = (props) => {
1542
- const { theme, errorColor, hasErrorMessage, disabled } = props;
1543
- const borderColor = hasErrorMessage ? errorColor : theme.palette.neutralSecondary;
1544
- const borderColorActive = hasErrorMessage ? errorColor : theme.palette.themePrimary;
1542
+ const { theme, hasErrorMessage, disabled } = props;
1543
+ const borderColor = hasErrorMessage ? theme.semanticColors.errorText : theme.palette.neutralSecondary;
1544
+ const borderColorActive = hasErrorMessage ? theme.semanticColors.errorText : theme.palette.themePrimary;
1545
1545
  const borderThickness = disabled ? 0 : defaultSendBoxInactiveBorderThicknessREM;
1546
1546
  const borderActiveThickness = disabled ? 0 : defaultSendBoxActiveBorderThicknessREM;
1547
1547
  return {
@@ -1594,6 +1594,9 @@ const lightTheme = {
1594
1594
  callRedDark: '#8b2c3d',
1595
1595
  callRedDarker: '#772a38',
1596
1596
  iconWhite: '#ffffff'
1597
+ },
1598
+ semanticColors: {
1599
+ errorText: '#a80000'
1597
1600
  }
1598
1601
  };
1599
1602
  /**
@@ -1631,6 +1634,9 @@ const darkTheme = {
1631
1634
  callRedDark: '#a42e43',
1632
1635
  callRedDarker: '#8b2c3d',
1633
1636
  iconWhite: '#ffffff'
1637
+ },
1638
+ semanticColors: {
1639
+ errorText: '#f1707b'
1634
1640
  }
1635
1641
  };
1636
1642
 
@@ -1822,31 +1828,26 @@ const textContainerStyle = {
1822
1828
  /**
1823
1829
  * @private
1824
1830
  */
1825
- const textFieldStyle = (errorColor) => {
1826
- return {
1827
- root: {
1828
- width: '100%',
1829
- minHeight: '0',
1830
- fontSize: '8.25rem'
1831
- },
1832
- wrapper: {},
1833
- fieldGroup: {
1834
- outline: 'none',
1835
- border: 'none',
1836
- height: 'auto',
1837
- minHeight: '0',
1838
- /**
1839
- * Removing fieldGroup border to make sure no border is rendered around the text area in sendbox.
1840
- */
1841
- ':after': { border: 'none' }
1842
- },
1843
- field: {
1844
- borderRadius: '0.25rem'
1845
- },
1846
- errorMessage: {
1847
- color: errorColor
1848
- }
1849
- };
1831
+ const textFieldStyle = {
1832
+ root: {
1833
+ width: '100%',
1834
+ minHeight: '0',
1835
+ fontSize: '8.25rem'
1836
+ },
1837
+ wrapper: {},
1838
+ fieldGroup: {
1839
+ outline: 'none',
1840
+ border: 'none',
1841
+ height: 'auto',
1842
+ minHeight: '0',
1843
+ /**
1844
+ * Removing fieldGroup border to make sure no border is rendered around the text area in sendbox.
1845
+ */
1846
+ ':after': { border: 'none' }
1847
+ },
1848
+ field: {
1849
+ borderRadius: '0.25rem'
1850
+ }
1850
1851
  };
1851
1852
  /**
1852
1853
  * @private
@@ -1919,11 +1920,10 @@ const getPerceptualBrightnessOfHexColor = (hexColor) => {
1919
1920
  */
1920
1921
  const InputBoxComponent = (props) => {
1921
1922
  const { styles, id, 'data-ui-id': dataUiId, textValue, onChange, textFieldRef, placeholderText, onKeyDown, onEnterKeyDown, supportNewline, inputClassName, errorMessage, disabled, children } = props;
1922
- const theme = useTheme();
1923
1923
  const mergedRootStyle = react.mergeStyles(inputBoxWrapperStyle, styles === null || styles === void 0 ? void 0 : styles.root);
1924
1924
  const mergedTextFiledStyle = react.mergeStyles(inputBoxStyle, inputClassName, props.inlineChildren ? {} : inputBoxNewLineSpaceAffordance);
1925
1925
  const mergedTextContainerStyle = react.mergeStyles(textContainerStyle, styles === null || styles === void 0 ? void 0 : styles.textFieldContainer);
1926
- const mergedTextFieldStyle = react.concatStyleSets(textFieldStyle(isDarkThemed(theme) ? '#f1707b' : '#a80000'), {
1926
+ const mergedTextFieldStyle = react.concatStyleSets(textFieldStyle, {
1927
1927
  fieldGroup: styles === null || styles === void 0 ? void 0 : styles.textField,
1928
1928
  errorMessage: styles === null || styles === void 0 ? void 0 : styles.systemMessage
1929
1929
  });
@@ -2233,7 +2233,6 @@ const SendBox = (props) => {
2233
2233
  React__default['default'].createElement(SendBoxErrors, Object.assign({}, sendBoxErrorsProps)),
2234
2234
  React__default['default'].createElement(react.Stack, { className: react.mergeStyles(borderAndBoxShadowStyle({
2235
2235
  theme,
2236
- errorColor: isDarkThemed(theme) ? '#f1707b' : '#a80000',
2237
2236
  hasErrorMessage: !!errorMessage,
2238
2237
  disabled: !!disabled
2239
2238
  })) },
@@ -2581,9 +2580,10 @@ const onRenderSubmitIcon = (color) => {
2581
2580
  const ChatMessageComponentAsEditBox = (props) => {
2582
2581
  const { onCancel, onSubmit, strings, message } = props;
2583
2582
  const [textValue, setTextValue] = React.useState(message.content || '');
2583
+ const [attachedFilesMetadata, setAttachedFilesMetadata] = React__default['default'].useState(getMessageAttachedFilesMetadata(message));
2584
2584
  const editTextFieldRef = React__default['default'].useRef(null);
2585
2585
  const theme = useTheme();
2586
- const messageState = getMessageState(textValue);
2586
+ const messageState = getMessageState(textValue, attachedFilesMetadata !== null && attachedFilesMetadata !== void 0 ? attachedFilesMetadata : []);
2587
2587
  const submitEnabled = messageState === 'OK';
2588
2588
  React.useEffect(() => {
2589
2589
  var _a;
@@ -2600,38 +2600,47 @@ const ChatMessageComponentAsEditBox = (props) => {
2600
2600
  const editBoxStyles = React.useMemo(() => {
2601
2601
  return react.concatStyleSets(editBoxStyleSet, { textField: { borderColor: theme.palette.themePrimary } });
2602
2602
  }, [theme.palette.themePrimary]);
2603
- /* @conditional-compile-remove(file-sharing) */
2604
2603
  const onRenderFileUploads = React.useCallback(() => {
2605
- var _a;
2606
- return (React__default['default'].createElement("div", { style: { margin: '0.25rem' } },
2607
- React__default['default'].createElement(_FileUploadCards, { activeFileUploads: (_a = message.attachedFilesMetadata) === null || _a === void 0 ? void 0 : _a.map((file) => ({
2604
+ return ((attachedFilesMetadata === null || attachedFilesMetadata === void 0 ? void 0 : attachedFilesMetadata.length) && (React__default['default'].createElement("div", { style: { margin: '0.25rem' } },
2605
+ React__default['default'].createElement(_FileUploadCards, { activeFileUploads: attachedFilesMetadata === null || attachedFilesMetadata === void 0 ? void 0 : attachedFilesMetadata.map((file) => ({
2608
2606
  id: file.name,
2609
2607
  filename: file.name,
2610
2608
  progress: 1
2611
- })) })));
2612
- }, [message.attachedFilesMetadata]);
2609
+ })), onCancelFileUpload: (fileId) => {
2610
+ setAttachedFilesMetadata(attachedFilesMetadata === null || attachedFilesMetadata === void 0 ? void 0 : attachedFilesMetadata.filter((file) => file.name !== fileId));
2611
+ } }))));
2612
+ }, [attachedFilesMetadata]);
2613
2613
  return (React__default['default'].createElement(react.Stack, { className: react.mergeStyles(borderAndBoxShadowStyle({
2614
2614
  theme,
2615
- errorColor: isDarkThemed(theme) ? '#f1707b' : '#a80000',
2616
2615
  hasErrorMessage: false,
2617
2616
  disabled: false
2618
2617
  })) },
2619
2618
  React__default['default'].createElement(InputBoxComponent, { inlineChildren: props.inlineEditButtons, id: 'editbox', textFieldRef: editTextFieldRef, inputClassName: editBoxStyle(props.inlineEditButtons), placeholderText: strings.editBoxPlaceholderText, textValue: textValue, onChange: setText, onEnterKeyDown: () => {
2620
- submitEnabled && onSubmit(textValue);
2619
+ submitEnabled &&
2620
+ onSubmit(textValue, message.metadata, {
2621
+ attachedFilesMetadata
2622
+ });
2621
2623
  }, supportNewline: false, maxLength: MAXIMUM_LENGTH_OF_MESSAGE, errorMessage: textTooLongMessage, styles: editBoxStyles },
2622
2624
  React__default['default'].createElement(InputBoxButton, { className: editingButtonStyle, ariaLabel: strings.editBoxCancelButton, tooltipContent: strings.editBoxCancelButton, onRenderIcon: onRenderThemedCancelIcon, onClick: () => {
2623
2625
  onCancel && onCancel();
2624
2626
  }, id: 'dismissIconWrapper' }),
2625
2627
  React__default['default'].createElement(InputBoxButton, { className: editingButtonStyle, ariaLabel: strings.editBoxSubmitButton, tooltipContent: strings.editBoxSubmitButton, onRenderIcon: onRenderThemedSubmitIcon, onClick: (e) => {
2626
- submitEnabled && onSubmit(textValue);
2628
+ submitEnabled &&
2629
+ onSubmit(textValue, message.metadata, {
2630
+ attachedFilesMetadata
2631
+ });
2627
2632
  e.stopPropagation();
2628
2633
  }, id: 'submitIconWrapper' })),
2629
- /* @conditional-compile-remove(file-sharing) */
2630
2634
  onRenderFileUploads()));
2631
2635
  };
2632
2636
  const isMessageTooLong = (messageText) => messageText.length > MAXIMUM_LENGTH_OF_MESSAGE;
2633
- const isMessageEmpty = (messageText) => messageText.trim().length === 0;
2634
- const getMessageState = (messageText) => isMessageEmpty(messageText) ? 'too short' : isMessageTooLong(messageText) ? 'too long' : 'OK';
2637
+ const isMessageEmpty = (messageText, attachedFilesMetadata) => messageText.trim().length === 0 && attachedFilesMetadata.length === 0;
2638
+ const getMessageState = (messageText, attachedFilesMetadata) => isMessageEmpty(messageText, attachedFilesMetadata) ? 'too short' : isMessageTooLong(messageText) ? 'too long' : 'OK';
2639
+ // @TODO: Remove when file-sharing feature becomes stable.
2640
+ const getMessageAttachedFilesMetadata = (message) => {
2641
+ /* @conditional-compile-remove(file-sharing) */
2642
+ return message.attachedFilesMetadata;
2643
+ };
2635
2644
 
2636
2645
  // Copyright (c) Microsoft Corporation.
2637
2646
  const MINIMUM_TOUCH_TARGET_HEIGHT_REM$1 = 3;
@@ -2679,7 +2688,7 @@ const chatMessageEditedTagStyle = (theme) => react.mergeStyles({ fontWeight: rea
2679
2688
  /**
2680
2689
  * @private
2681
2690
  */
2682
- const chatMessageFailedTagStyle = (theme) => react.mergeStyles({ fontWeight: react.FontWeights.light, color: isDarkThemed(theme) ? '#f1707b' : '#a80000' });
2691
+ const chatMessageFailedTagStyle = (theme) => react.mergeStyles({ fontWeight: react.FontWeights.light, color: theme.semanticColors.errorText });
2683
2692
  /**
2684
2693
  * @private
2685
2694
  */
@@ -3220,10 +3229,10 @@ const ChatMessageComponent = (props) => {
3220
3229
  return React__default['default'].createElement(React__default['default'].Fragment, null);
3221
3230
  }
3222
3231
  else if (isEditing) {
3223
- return (React__default['default'].createElement(ChatMessageComponentAsEditBox, { message: message, inlineEditButtons: props.inlineAcceptRejectEditButtons, strings: props.strings, onSubmit: (text) => __awaiter$o(void 0, void 0, void 0, function* () {
3232
+ return (React__default['default'].createElement(ChatMessageComponentAsEditBox, { message: message, inlineEditButtons: props.inlineAcceptRejectEditButtons, strings: props.strings, onSubmit: (text, metadata, options) => __awaiter$o(void 0, void 0, void 0, function* () {
3224
3233
  props.onUpdateMessage &&
3225
3234
  props.message.messageId &&
3226
- (yield props.onUpdateMessage(props.message.messageId, text));
3235
+ (yield props.onUpdateMessage(props.message.messageId, text, metadata, options));
3227
3236
  setIsEditing(false);
3228
3237
  }), onCancel: () => {
3229
3238
  setIsEditing(false);
@@ -4329,6 +4338,8 @@ const VideoTile = (props) => {
4329
4338
  * A memoized version of VideoTile for rendering remote participants. React.memo is used for a performance
4330
4339
  * boost by memoizing the same rendered component to avoid rerendering a VideoTile when its position in the
4331
4340
  * array changes causing a rerender in the parent component. https://reactjs.org/docs/react-api.html#reactmemo
4341
+ *
4342
+ * @private
4332
4343
  */
4333
4344
  const RemoteVideoTile = React__default['default'].memo((props) => {
4334
4345
  const { isAvailable, isMuted, isSpeaking, isScreenSharingOn, onCreateRemoteStreamView, onDisposeRemoteStreamView, remoteVideoViewOptions, renderElement, userId, displayName, onRenderAvatar, showMuteIndicator } = props;
@@ -4805,25 +4816,6 @@ const LoadingSpinner = (props) => {
4805
4816
  React__default['default'].createElement(react.Spinner, { label: props.loadingMessage, size: react.SpinnerSize.xSmall, "aria-live": 'assertive' })));
4806
4817
  };
4807
4818
 
4808
- // Copyright (c) Microsoft Corporation.
4809
- /**
4810
- * local video tile camera cycle button - for use on mobile screens only.
4811
- * @internal
4812
- */
4813
- const LocalVideoCameraCycleButton = (props) => {
4814
- const { cameras, selectedCamera, onSelectCamera, label, ariaDescription } = props;
4815
- const theme = react.useTheme();
4816
- return (React__default['default'].createElement(react.IconButton, { "data-ui-id": 'local-camera-switcher-button', styles: localVideoCameraCycleButtonStyles(theme), iconProps: { iconName: 'LocalCameraSwitch' }, ariaLabel: label, ariaDescription: ariaDescription, "aria-live": 'polite', onClick: () => {
4817
- if (cameras && cameras.length > 1 && selectedCamera !== undefined) {
4818
- const index = cameras.findIndex((camera) => selectedCamera.id === camera.id);
4819
- const newCamera = cameras[(index + 1) % cameras.length];
4820
- if (onSelectCamera !== undefined) {
4821
- onSelectCamera(newCamera);
4822
- }
4823
- }
4824
- } }));
4825
- };
4826
-
4827
4819
  // Copyright (c) Microsoft Corporation.
4828
4820
  const animationDuration = react.AnimationVariables.durationValue2;
4829
4821
  const ZERO = { x: 0, y: 0 };
@@ -5496,6 +5488,84 @@ const _ModalClone = react.styled(ModalBase, getStyles, undefined, {
5496
5488
  });
5497
5489
  _ModalClone.displayName = 'Modal';
5498
5490
 
5491
+ // Copyright (c) Microsoft Corporation.
5492
+ /**
5493
+ * local video tile camera cycle button - for use on mobile screens only.
5494
+ * @internal
5495
+ */
5496
+ const LocalVideoCameraCycleButton = (props) => {
5497
+ const { cameras, selectedCamera, onSelectCamera, label, ariaDescription } = props;
5498
+ const theme = react.useTheme();
5499
+ return (React__default['default'].createElement(react.IconButton, { "data-ui-id": 'local-camera-switcher-button', styles: localVideoCameraCycleButtonStyles(theme), iconProps: { iconName: 'LocalCameraSwitch' }, ariaLabel: label, ariaDescription: ariaDescription, "aria-live": 'polite', onClick: () => {
5500
+ if (cameras && cameras.length > 1 && selectedCamera !== undefined) {
5501
+ const index = cameras.findIndex((camera) => selectedCamera.id === camera.id);
5502
+ const newCamera = cameras[(index + 1) % cameras.length];
5503
+ if (onSelectCamera !== undefined) {
5504
+ onSelectCamera(newCamera);
5505
+ }
5506
+ }
5507
+ } }));
5508
+ };
5509
+
5510
+ // Copyright (c) Microsoft Corporation.
5511
+ /**
5512
+ * A memoized version of VideoTile for rendering local participant.
5513
+ *
5514
+ * @private
5515
+ */
5516
+ const LocalVideoTile = React__default['default'].memo((props) => {
5517
+ const { isAvailable, isMuted, onCreateLocalStreamView, onDisposeLocalStreamView, localVideoViewOptions, renderElement, userId, showLabel, displayName, initialsName, onRenderAvatar, showMuteIndicator, styles, showCameraSwitcherInLocalPreview, localVideoCameraCycleButtonProps, localVideoCameraSwitcherLabel, localVideoSelectedDescription } = props;
5518
+ React.useEffect(() => {
5519
+ if (isAvailable && !renderElement) {
5520
+ onCreateLocalStreamView === null || onCreateLocalStreamView === void 0 ? void 0 : onCreateLocalStreamView(localVideoViewOptions);
5521
+ }
5522
+ // Always clean up element to make tile up to date and be able to dispose correctly
5523
+ return () => {
5524
+ if (renderElement) {
5525
+ onDisposeLocalStreamView === null || onDisposeLocalStreamView === void 0 ? void 0 : onDisposeLocalStreamView();
5526
+ }
5527
+ };
5528
+ }, [isAvailable, onCreateLocalStreamView, onDisposeLocalStreamView, localVideoViewOptions, renderElement]);
5529
+ // The execution order for above useEffect is onCreateRemoteStreamView =>(async time gap) RenderElement generated => element disposed => onDisposeRemoteStreamView
5530
+ // Element disposed could happen during async time gap, which still cause leaks for unused renderElement.
5531
+ // Need to do an entire cleanup when remoteTile gets disposed and make sure element gets correctly disposed
5532
+ React.useEffect(() => {
5533
+ return () => {
5534
+ onDisposeLocalStreamView === null || onDisposeLocalStreamView === void 0 ? void 0 : onDisposeLocalStreamView();
5535
+ };
5536
+ }, [onDisposeLocalStreamView]);
5537
+ const renderVideoStreamElement = React.useMemo(() => {
5538
+ // Checking if renderElement is well defined or not as calling SDK has a number of video streams limitation which
5539
+ // implies that, after their threshold, all streams have no child (blank video)
5540
+ if (!renderElement || !renderElement.childElementCount) {
5541
+ // Returning `undefined` results in the placeholder with avatar being shown
5542
+ return undefined;
5543
+ }
5544
+ return (React__default['default'].createElement(React__default['default'].Fragment, null,
5545
+ React__default['default'].createElement(FloatingLocalCameraCycleButton, { showCameraSwitcherInLocalPreview: showCameraSwitcherInLocalPreview !== null && showCameraSwitcherInLocalPreview !== void 0 ? showCameraSwitcherInLocalPreview : false, localVideoCameraCycleButtonProps: localVideoCameraCycleButtonProps, localVideoCameraSwitcherLabel: localVideoCameraSwitcherLabel, localVideoSelectedDescription: localVideoSelectedDescription }),
5546
+ React__default['default'].createElement(StreamMedia, { videoStreamElement: renderElement })));
5547
+ }, [
5548
+ localVideoCameraCycleButtonProps,
5549
+ localVideoCameraSwitcherLabel,
5550
+ localVideoSelectedDescription,
5551
+ renderElement,
5552
+ showCameraSwitcherInLocalPreview
5553
+ ]);
5554
+ return (React__default['default'].createElement(VideoTile, { key: userId, userId: userId, renderElement: renderVideoStreamElement, showLabel: showLabel, displayName: displayName, initialsName: initialsName, styles: styles, onRenderPlaceholder: onRenderAvatar, isMuted: isMuted, showMuteIndicator: showMuteIndicator }));
5555
+ });
5556
+ const FloatingLocalCameraCycleButton = (props) => {
5557
+ const { showCameraSwitcherInLocalPreview, localVideoCameraCycleButtonProps, localVideoCameraSwitcherLabel, localVideoSelectedDescription } = props;
5558
+ const ariaDescription = (localVideoCameraCycleButtonProps === null || localVideoCameraCycleButtonProps === void 0 ? void 0 : localVideoCameraCycleButtonProps.selectedCamera) &&
5559
+ localVideoSelectedDescription &&
5560
+ _formatString(localVideoSelectedDescription, {
5561
+ cameraName: localVideoCameraCycleButtonProps.selectedCamera.name
5562
+ });
5563
+ return (React__default['default'].createElement(react.Stack, { horizontalAlign: "end" }, showCameraSwitcherInLocalPreview &&
5564
+ (localVideoCameraCycleButtonProps === null || localVideoCameraCycleButtonProps === void 0 ? void 0 : localVideoCameraCycleButtonProps.cameras) !== undefined &&
5565
+ (localVideoCameraCycleButtonProps === null || localVideoCameraCycleButtonProps === void 0 ? void 0 : localVideoCameraCycleButtonProps.selectedCamera) !== undefined &&
5566
+ (localVideoCameraCycleButtonProps === null || localVideoCameraCycleButtonProps === void 0 ? void 0 : localVideoCameraCycleButtonProps.onSelectCamera) !== undefined && (React__default['default'].createElement(LocalVideoCameraCycleButton, { cameras: localVideoCameraCycleButtonProps.cameras, selectedCamera: localVideoCameraCycleButtonProps.selectedCamera, onSelectCamera: localVideoCameraCycleButtonProps.onSelectCamera, label: localVideoCameraSwitcherLabel, ariaDescription: ariaDescription }))));
5567
+ };
5568
+
5499
5569
  // Copyright (c) Microsoft Corporation.
5500
5570
  // Currently the Calling JS SDK supports up to 4 remote video streams
5501
5571
  const DEFAULT_MAX_REMOTE_VIDEO_STREAMS = 4;
@@ -5520,7 +5590,7 @@ const modalMaxDragPosition = { x: localVideoTileOuterPaddingPX, y: localVideoTil
5520
5590
  */
5521
5591
  const VideoGallery = (props) => {
5522
5592
  var _a, _b, _c;
5523
- const { localParticipant, remoteParticipants = [], localVideoViewOptions, remoteVideoViewOptions, dominantSpeakers, onRenderLocalVideoTile, onRenderRemoteVideoTile, onCreateLocalStreamView, onCreateRemoteStreamView, onDisposeRemoteStreamView, styles, layout, onRenderAvatar, showMuteIndicator, maxRemoteVideoStreams = DEFAULT_MAX_REMOTE_VIDEO_STREAMS, showCameraSwitcherInLocalPreview, localVideoCameraCycleButtonProps } = props;
5593
+ const { localParticipant, remoteParticipants = [], localVideoViewOptions, remoteVideoViewOptions, dominantSpeakers, onRenderLocalVideoTile, onRenderRemoteVideoTile, onCreateLocalStreamView, onDisposeLocalStreamView, onCreateRemoteStreamView, onDisposeRemoteStreamView, styles, layout, onRenderAvatar, showMuteIndicator, maxRemoteVideoStreams = DEFAULT_MAX_REMOTE_VIDEO_STREAMS, showCameraSwitcherInLocalPreview, localVideoCameraCycleButtonProps } = props;
5524
5594
  const ids = useIdentifiers();
5525
5595
  const theme = useTheme();
5526
5596
  const localeStrings = useLocale$1().strings.videoGallery;
@@ -5559,56 +5629,37 @@ const VideoGallery = (props) => {
5559
5629
  lastVisibleParticipants: visibleAudioParticipants.current,
5560
5630
  maxDominantSpeakers: MAX_AUDIO_DOMINANT_SPEAKERS
5561
5631
  });
5562
- const LocalCameraCycleButton = React.useCallback(() => {
5563
- const ariaDescription = (localVideoCameraCycleButtonProps === null || localVideoCameraCycleButtonProps === void 0 ? void 0 : localVideoCameraCycleButtonProps.selectedCamera) &&
5564
- _formatString(strings.localVideoSelectedDescription, {
5565
- cameraName: localVideoCameraCycleButtonProps.selectedCamera.name
5566
- });
5567
- return (React__default['default'].createElement(react.Stack, { horizontalAlign: "end" }, showCameraSwitcherInLocalPreview &&
5568
- (localVideoCameraCycleButtonProps === null || localVideoCameraCycleButtonProps === void 0 ? void 0 : localVideoCameraCycleButtonProps.cameras) !== undefined &&
5569
- (localVideoCameraCycleButtonProps === null || localVideoCameraCycleButtonProps === void 0 ? void 0 : localVideoCameraCycleButtonProps.selectedCamera) !== undefined &&
5570
- (localVideoCameraCycleButtonProps === null || localVideoCameraCycleButtonProps === void 0 ? void 0 : localVideoCameraCycleButtonProps.onSelectCamera) !== undefined && (React__default['default'].createElement(LocalVideoCameraCycleButton, { cameras: localVideoCameraCycleButtonProps.cameras, selectedCamera: localVideoCameraCycleButtonProps.selectedCamera, onSelectCamera: localVideoCameraCycleButtonProps.onSelectCamera, label: strings.localVideoCameraSwitcherLabel, ariaDescription: ariaDescription }))));
5571
- }, [
5572
- localVideoCameraCycleButtonProps === null || localVideoCameraCycleButtonProps === void 0 ? void 0 : localVideoCameraCycleButtonProps.cameras,
5573
- localVideoCameraCycleButtonProps === null || localVideoCameraCycleButtonProps === void 0 ? void 0 : localVideoCameraCycleButtonProps.onSelectCamera,
5574
- localVideoCameraCycleButtonProps === null || localVideoCameraCycleButtonProps === void 0 ? void 0 : localVideoCameraCycleButtonProps.selectedCamera,
5575
- showCameraSwitcherInLocalPreview,
5576
- strings.localVideoCameraSwitcherLabel,
5577
- strings.localVideoSelectedDescription
5578
- ]);
5579
5632
  /**
5580
5633
  * Utility function for memoized rendering of LocalParticipant.
5581
5634
  */
5582
5635
  const localVideoTile = React.useMemo(() => {
5583
- const localVideoStream = localParticipant === null || localParticipant === void 0 ? void 0 : localParticipant.videoStream;
5636
+ var _a, _b;
5584
5637
  if (onRenderLocalVideoTile) {
5585
5638
  return onRenderLocalVideoTile(localParticipant);
5586
5639
  }
5587
- const localVideoTileStyles = shouldFloatLocalVideo ? floatingLocalVideoTileStyle : {};
5588
- const localVideoTileStylesThemed = react.concatStyleSets(localVideoTileStyles, {
5640
+ const localVideoTileStyles = react.concatStyleSets(shouldFloatLocalVideo ? floatingLocalVideoTileStyle : {}, {
5589
5641
  root: { borderRadius: theme.effects.roundedCorner4 }
5590
5642
  }, styles === null || styles === void 0 ? void 0 : styles.localVideo);
5591
- if (localVideoStream && !localVideoStream.renderElement) {
5592
- onCreateLocalStreamView && onCreateLocalStreamView(localVideoViewOptions);
5593
- }
5594
5643
  return (React__default['default'].createElement(react.Stack, { tabIndex: 0, "aria-label": strings.localVideoMovementLabel, role: 'dialog' },
5595
- React__default['default'].createElement(VideoTile, { key: localParticipant.userId, userId: localParticipant.userId, renderElement: (localVideoStream === null || localVideoStream === void 0 ? void 0 : localVideoStream.renderElement) ? (React__default['default'].createElement(React__default['default'].Fragment, null,
5596
- React__default['default'].createElement(LocalCameraCycleButton, null),
5597
- React__default['default'].createElement(StreamMedia, { videoStreamElement: localVideoStream.renderElement }))) : undefined, showLabel: !(shouldFloatLocalVideo && isNarrow), displayName: isNarrow ? '' : strings.localVideoLabel, initialsName: localParticipant.displayName, styles: localVideoTileStylesThemed, onRenderPlaceholder: onRenderAvatar, isMuted: localParticipant.isMuted, showMuteIndicator: showMuteIndicator })));
5644
+ React__default['default'].createElement(LocalVideoTile, { userId: localParticipant.userId, onCreateLocalStreamView: onCreateLocalStreamView, onDisposeLocalStreamView: onDisposeLocalStreamView, isAvailable: (_a = localParticipant === null || localParticipant === void 0 ? void 0 : localParticipant.videoStream) === null || _a === void 0 ? void 0 : _a.isAvailable, isMuted: localParticipant.isMuted, renderElement: (_b = localParticipant === null || localParticipant === void 0 ? void 0 : localParticipant.videoStream) === null || _b === void 0 ? void 0 : _b.renderElement, displayName: isNarrow ? '' : strings.localVideoLabel, initialsName: localParticipant.displayName, localVideoViewOptions: localVideoViewOptions, onRenderAvatar: onRenderAvatar, showLabel: !(shouldFloatLocalVideo && isNarrow), showMuteIndicator: showMuteIndicator, showCameraSwitcherInLocalPreview: showCameraSwitcherInLocalPreview, localVideoCameraCycleButtonProps: localVideoCameraCycleButtonProps, localVideoCameraSwitcherLabel: strings.localVideoCameraSwitcherLabel, localVideoSelectedDescription: strings.localVideoSelectedDescription, styles: localVideoTileStyles })));
5598
5645
  }, [
5599
- localParticipant,
5600
5646
  isNarrow,
5647
+ localParticipant,
5648
+ localVideoCameraCycleButtonProps,
5649
+ localVideoViewOptions,
5601
5650
  onCreateLocalStreamView,
5651
+ onDisposeLocalStreamView,
5652
+ onRenderAvatar,
5602
5653
  onRenderLocalVideoTile,
5603
- theme.effects.roundedCorner4,
5604
- styles === null || styles === void 0 ? void 0 : styles.localVideo,
5605
- strings.localVideoMovementLabel,
5606
- strings.localVideoLabel,
5607
- LocalCameraCycleButton,
5654
+ shouldFloatLocalVideo,
5655
+ showCameraSwitcherInLocalPreview,
5608
5656
  showMuteIndicator,
5609
- localVideoViewOptions,
5610
- onRenderAvatar,
5611
- shouldFloatLocalVideo
5657
+ strings.localVideoCameraSwitcherLabel,
5658
+ strings.localVideoLabel,
5659
+ strings.localVideoMovementLabel,
5660
+ strings.localVideoSelectedDescription,
5661
+ styles === null || styles === void 0 ? void 0 : styles.localVideo,
5662
+ theme.effects.roundedCorner4
5612
5663
  ]);
5613
5664
  const defaultOnRenderVideoTile = React.useCallback((participant, isVideoParticipant) => {
5614
5665
  const remoteVideoStream = participant.videoStream;
@@ -6715,6 +6766,240 @@ const _DrawerMenu = (props) => {
6715
6766
  } })))))));
6716
6767
  };
6717
6768
 
6769
+ // Copyright (c) Microsoft Corporation.
6770
+ // Licensed under the MIT license.
6771
+ /**
6772
+ * Return different conditions based on the current and previous state of recording and transcribing
6773
+ *
6774
+ * @param callRecordState - The current call record state: on, off, stopped
6775
+ * @param callTranscribeState - The current call transcribe state: on, off, stopped
6776
+ *
6777
+ * @remarks - The stopped state means: previously on but currently off
6778
+ *
6779
+ * @private
6780
+ */
6781
+ const computeVariant = (callRecordState, callTranscribeState) => {
6782
+ if (callRecordState === 'on' && callTranscribeState === 'on') {
6783
+ return 'RECORDING_AND_TRANSCRIPTION_STARTED';
6784
+ }
6785
+ else if (callRecordState === 'on' && callTranscribeState === 'off') {
6786
+ return 'RECORDING_STARTED';
6787
+ }
6788
+ else if (callRecordState === 'off' && callTranscribeState === 'on') {
6789
+ return 'TRANSCRIPTION_STARTED';
6790
+ }
6791
+ else if (callRecordState === 'on' && callTranscribeState === 'stopped') {
6792
+ return 'TRANSCRIPTION_STOPPED_STILL_RECORDING';
6793
+ }
6794
+ else if (callRecordState === 'stopped' && callTranscribeState === 'on') {
6795
+ return 'RECORDING_STOPPED_STILL_TRANSCRIBING';
6796
+ }
6797
+ else if (callRecordState === 'off' && callTranscribeState === 'stopped') {
6798
+ return 'TRANSCRIPTION_STOPPED';
6799
+ }
6800
+ else if (callRecordState === 'stopped' && callTranscribeState === 'off') {
6801
+ return 'RECORDING_STOPPED';
6802
+ }
6803
+ else if (callRecordState === 'stopped' && callTranscribeState === 'stopped') {
6804
+ return 'RECORDING_AND_TRANSCRIPTION_STOPPED';
6805
+ }
6806
+ else {
6807
+ return 'NO_STATE';
6808
+ }
6809
+ };
6810
+
6811
+ // Copyright (c) Microsoft Corporation.
6812
+ const BANNER_OVERWRITE_DELAY_MS = 3000;
6813
+ /**
6814
+ * A component that displays banners to notify the user when call recording and
6815
+ * transcription is enabled or disabled in a call.
6816
+ *
6817
+ * This component implements a state machine that tracks the changes to call
6818
+ * recording and transcription state and shows the corresponding message.
6819
+ *
6820
+ * @internal
6821
+ */
6822
+ const _ComplianceBanner = (props) => {
6823
+ const cachedProps = React.useRef({
6824
+ latestBooleanState: {
6825
+ callTranscribeState: false,
6826
+ callRecordState: false
6827
+ },
6828
+ latestStringState: {
6829
+ callTranscribeState: 'off',
6830
+ callRecordState: 'off'
6831
+ },
6832
+ lastUpdated: Date.now()
6833
+ });
6834
+ // Only update cached props and variant if there is _some_ change in the latest props.
6835
+ // This ensures that state machine is only updated if there is an actual change in the props.
6836
+ const shouldUpdateCached = props.callRecordState !== cachedProps.current.latestBooleanState.callRecordState ||
6837
+ props.callTranscribeState !== cachedProps.current.latestBooleanState.callTranscribeState;
6838
+ // The following three operations must be performed in this exact order:
6839
+ // [1]: Update cached state to transition the state machine.
6840
+ if (shouldUpdateCached) {
6841
+ cachedProps.current = {
6842
+ latestBooleanState: props,
6843
+ latestStringState: {
6844
+ callRecordState: determineStates(cachedProps.current.latestStringState.callRecordState, props.callRecordState),
6845
+ callTranscribeState: determineStates(cachedProps.current.latestStringState.callTranscribeState, props.callTranscribeState)
6846
+ },
6847
+ lastUpdated: Date.now()
6848
+ };
6849
+ }
6850
+ // [2]: Compute the variant, using the transitioned state machine.
6851
+ const variant = computeVariant(cachedProps.current.latestStringState.callRecordState, cachedProps.current.latestStringState.callTranscribeState);
6852
+ // [3]: Transition the state machine again to deal with some end-states.
6853
+ if (shouldUpdateCached &&
6854
+ cachedProps.current.latestStringState.callRecordState === 'stopped' &&
6855
+ cachedProps.current.latestStringState.callTranscribeState === 'stopped') {
6856
+ // When both states are stopped, after displaying message "RECORDING_AND_TRANSCRIPTION_STOPPED", change both states to off (going back to the default state).
6857
+ cachedProps.current.latestStringState.callRecordState = 'off';
6858
+ cachedProps.current.latestStringState.callTranscribeState = 'off';
6859
+ }
6860
+ return (React__default['default'].createElement(DelayedUpdateBanner, { variant: {
6861
+ variant,
6862
+ lastUpdated: cachedProps.current.lastUpdated
6863
+ }, strings: props.strings, onDismiss: () => {
6864
+ if (cachedProps.current.latestStringState.callRecordState === 'stopped') {
6865
+ cachedProps.current.latestStringState.callRecordState = 'off';
6866
+ }
6867
+ if (cachedProps.current.latestStringState.callTranscribeState === 'stopped') {
6868
+ cachedProps.current.latestStringState.callTranscribeState = 'off';
6869
+ }
6870
+ } }));
6871
+ };
6872
+ function determineStates(previous, current) {
6873
+ // if current state is on, then return on
6874
+ if (current) {
6875
+ return 'on';
6876
+ }
6877
+ // if current state is off
6878
+ else {
6879
+ // if previous state is on and current state is off, return stopped (on -> off)
6880
+ if (previous === 'on') {
6881
+ return 'stopped';
6882
+ }
6883
+ // otherwise remain previous state unchanged
6884
+ else {
6885
+ return previous;
6886
+ }
6887
+ }
6888
+ }
6889
+ /**
6890
+ * Shows a {@link BannerMessage} in a {@link MessageBar} tracking `variant` internally.
6891
+ *
6892
+ * This component delays and combines frequent updates to `variant` such that:
6893
+ * - Updates that happen within {@link BANNER_OVERWRITE_DELAY_MS} are delayed.
6894
+ * - Once {@link BANNER_OVERWRITE_DELAY_MS} has passed since the last update, the _latest_ pending update is shown.
6895
+ *
6896
+ * This ensures that there is enough time for the user to see a banner message before it is overwritten.
6897
+ * In case of multiple delayed messages, the user always sees the final message as it reflects the final state
6898
+ * of recording and transcription.
6899
+ *
6900
+ * @private
6901
+ */
6902
+ function DelayedUpdateBanner(props) {
6903
+ const { variant, lastUpdated: variantLastUpdated } = props.variant;
6904
+ // Tracks the variant that is currently visible in the UI.
6905
+ const [visible, setVisible] = React.useState({
6906
+ variant,
6907
+ lastUpdated: Date.now()
6908
+ });
6909
+ const pendingUpdateHandle = React.useRef(null);
6910
+ if (variant !== visible.variant && variantLastUpdated > visible.lastUpdated) {
6911
+ // Always clear pending updates.
6912
+ // We'll either update now, or schedule an update for later.
6913
+ if (pendingUpdateHandle.current) {
6914
+ clearTimeout(pendingUpdateHandle.current);
6915
+ pendingUpdateHandle.current = null;
6916
+ }
6917
+ const now = Date.now();
6918
+ const timeToNextUpdate = BANNER_OVERWRITE_DELAY_MS - (now - visible.lastUpdated);
6919
+ if (variant === 'NO_STATE' || timeToNextUpdate <= 0) {
6920
+ setVisible({
6921
+ variant,
6922
+ lastUpdated: now
6923
+ });
6924
+ }
6925
+ else {
6926
+ pendingUpdateHandle.current = setTimeout(() => {
6927
+ // Set the actual update time, not the computed time when the update should happen.
6928
+ // The actual update might be later than we planned.
6929
+ setVisible({
6930
+ variant,
6931
+ lastUpdated: Date.now()
6932
+ });
6933
+ }, timeToNextUpdate);
6934
+ }
6935
+ }
6936
+ if (visible.variant === 'NO_STATE') {
6937
+ return React__default['default'].createElement(React__default['default'].Fragment, null);
6938
+ }
6939
+ return (React__default['default'].createElement(react.MessageBar, { messageBarType: react.MessageBarType.warning, onDismiss: () => {
6940
+ // when closing the banner, change variant to nostate and change stopped state to off state.
6941
+ // Reason: on banner close, going back to the default state.
6942
+ setVisible({
6943
+ variant: 'NO_STATE',
6944
+ lastUpdated: Date.now()
6945
+ });
6946
+ props.onDismiss();
6947
+ }, dismissButtonAriaLabel: props.strings.close },
6948
+ React__default['default'].createElement(BannerMessage, { variant: visible.variant, strings: props.strings })));
6949
+ }
6950
+ function BannerMessage(props) {
6951
+ const { variant, strings } = props;
6952
+ switch (variant) {
6953
+ case 'TRANSCRIPTION_STOPPED_STILL_RECORDING':
6954
+ return (React__default['default'].createElement(React__default['default'].Fragment, null,
6955
+ React__default['default'].createElement("b", null, strings.complianceBannerTranscriptionStopped),
6956
+ ` ${strings.complianceBannerNowOnlyRecording}`,
6957
+ React__default['default'].createElement(PrivacyPolicy, { linkText: strings.privacyPolicy })));
6958
+ case 'RECORDING_STOPPED_STILL_TRANSCRIBING':
6959
+ return (React__default['default'].createElement(React__default['default'].Fragment, null,
6960
+ React__default['default'].createElement("b", null, strings.complianceBannerRecordingStopped),
6961
+ ` ${strings.complianceBannerNowOnlyTranscription}`,
6962
+ React__default['default'].createElement(PrivacyPolicy, { linkText: strings.privacyPolicy })));
6963
+ case 'RECORDING_AND_TRANSCRIPTION_STOPPED':
6964
+ return (React__default['default'].createElement(React__default['default'].Fragment, null,
6965
+ React__default['default'].createElement("b", null, strings.complianceBannerRecordingAndTranscriptionSaved),
6966
+ ` ${strings.complianceBannerRecordingAndTranscriptionStopped}`,
6967
+ React__default['default'].createElement(LearnMore, { linkText: strings.learnMore })));
6968
+ case 'RECORDING_AND_TRANSCRIPTION_STARTED':
6969
+ return (React__default['default'].createElement(React__default['default'].Fragment, null,
6970
+ React__default['default'].createElement("b", null, strings.complianceBannerRecordingAndTranscriptionStarted),
6971
+ ` ${strings.complianceBannerTranscriptionConsent}`,
6972
+ React__default['default'].createElement(PrivacyPolicy, { linkText: strings.privacyPolicy })));
6973
+ case 'TRANSCRIPTION_STARTED':
6974
+ return (React__default['default'].createElement(React__default['default'].Fragment, null,
6975
+ React__default['default'].createElement("b", null, strings.complianceBannerTranscriptionStarted),
6976
+ ` ${strings.complianceBannerTranscriptionConsent}`,
6977
+ React__default['default'].createElement(PrivacyPolicy, { linkText: strings.privacyPolicy })));
6978
+ case 'RECORDING_STOPPED':
6979
+ return (React__default['default'].createElement(React__default['default'].Fragment, null,
6980
+ React__default['default'].createElement("b", null, strings.complianceBannerRecordingSaving),
6981
+ ` ${strings.complianceBannerRecordingStopped}`,
6982
+ React__default['default'].createElement(LearnMore, { linkText: strings.learnMore })));
6983
+ case 'RECORDING_STARTED':
6984
+ return (React__default['default'].createElement(React__default['default'].Fragment, null,
6985
+ React__default['default'].createElement("b", null, strings.complianceBannerRecordingStarted),
6986
+ ` ${strings.complianceBannerTranscriptionConsent}`,
6987
+ React__default['default'].createElement(PrivacyPolicy, { linkText: strings.privacyPolicy })));
6988
+ case 'TRANSCRIPTION_STOPPED':
6989
+ return (React__default['default'].createElement(React__default['default'].Fragment, null,
6990
+ React__default['default'].createElement("b", null, strings.complianceBannerTranscriptionSaving),
6991
+ ` ${strings.complianceBannerTranscriptionStopped}`,
6992
+ React__default['default'].createElement(LearnMore, { linkText: strings.learnMore })));
6993
+ }
6994
+ return React__default['default'].createElement(React__default['default'].Fragment, null);
6995
+ }
6996
+ function PrivacyPolicy(props) {
6997
+ return (React__default['default'].createElement(react.Link, { href: "https://privacy.microsoft.com/privacystatement#mainnoticetoendusersmodule", target: "_blank", underline: true }, props.linkText));
6998
+ }
6999
+ function LearnMore(props) {
7000
+ return (React__default['default'].createElement(react.Link, { href: "https://support.microsoft.com/office/record-a-meeting-in-teams-34dfbe7f-b07d-4a27-b4c6-de62f1348c24", target: "_blank", underline: true }, props.linkText));
7001
+ }
7002
+
6718
7003
  // Copyright (c) Microsoft Corporation.
6719
7004
  // Licensed under the MIT license.
6720
7005
  /**
@@ -7124,8 +7409,10 @@ const createDefaultChatHandlers = memoizeOne__default['default']((chatClient, ch
7124
7409
  };
7125
7410
  yield chatThreadClient.sendMessage(sendMessageRequest, options);
7126
7411
  }),
7127
- onUpdateMessage: (messageId, content, metadata) => __awaiter$k(void 0, void 0, void 0, function* () {
7128
- yield chatThreadClient.updateMessage(messageId, { content, metadata });
7412
+ onUpdateMessage: (messageId, content, metadata, options) => __awaiter$k(void 0, void 0, void 0, function* () {
7413
+ const updatedMetadata = metadata ? Object.assign({}, metadata) : {};
7414
+ updatedMetadata['fileSharingMetadata'] = JSON.stringify((options === null || options === void 0 ? void 0 : options.attachedFilesMetadata) || []);
7415
+ yield chatThreadClient.updateMessage(messageId, { content, metadata: updatedMetadata });
7129
7416
  }),
7130
7417
  onDeleteMessage: (messageId) => __awaiter$k(void 0, void 0, void 0, function* () {
7131
7418
  yield chatThreadClient.deleteMessage(messageId);
@@ -11448,10 +11735,11 @@ class AzureCommunicationChatAdapter {
11448
11735
  }));
11449
11736
  });
11450
11737
  }
11451
- updateMessage(messageId, content, metadata) {
11738
+ updateMessage(messageId, content, metadata, options) {
11452
11739
  return __awaiter$9(this, void 0, void 0, function* () {
11453
11740
  return yield this.asyncTeeErrorToEventEmitter(() => __awaiter$9(this, void 0, void 0, function* () {
11454
- return yield this.handlers.onUpdateMessage(messageId, content, metadata);
11741
+ /* @conditional-compile-remove(file-sharing) */
11742
+ return yield this.handlers.onUpdateMessage(messageId, content, metadata, options);
11455
11743
  }));
11456
11744
  });
11457
11745
  }
@@ -12745,45 +13033,6 @@ const getCallCompositePage = (call, previousCall) => {
12745
13033
  // No call state - show starting page (configuration)
12746
13034
  return 'configuration';
12747
13035
  };
12748
- /**
12749
- * Return different conditions based on the current and previous state of recording and transcribing
12750
- *
12751
- * @param callRecordState - The current call record state: on, off, stopped
12752
- * @param callTranscribeState - The current call transcribe state: on, off, stopped
12753
- *
12754
- * @remarks - The stopped state means: previously on but currently off
12755
- *
12756
- * @private
12757
- */
12758
- const computeVariant = (callRecordState, callTranscribeState) => {
12759
- if (callRecordState === 'on' && callTranscribeState === 'on') {
12760
- return 'RECORDING_AND_TRANSCRIPTION_STARTED';
12761
- }
12762
- else if (callRecordState === 'on' && callTranscribeState === 'off') {
12763
- return 'RECORDING_STARTED';
12764
- }
12765
- else if (callRecordState === 'off' && callTranscribeState === 'on') {
12766
- return 'TRANSCRIPTION_STARTED';
12767
- }
12768
- else if (callRecordState === 'on' && callTranscribeState === 'stopped') {
12769
- return 'TRANSCRIPTION_STOPPED_STILL_RECORDING';
12770
- }
12771
- else if (callRecordState === 'stopped' && callTranscribeState === 'on') {
12772
- return 'RECORDING_STOPPED_STILL_TRANSCRIBING';
12773
- }
12774
- else if (callRecordState === 'off' && callTranscribeState === 'stopped') {
12775
- return 'TRANSCRIPTION_STOPPED';
12776
- }
12777
- else if (callRecordState === 'stopped' && callTranscribeState === 'off') {
12778
- return 'RECORDING_STOPPED';
12779
- }
12780
- else if (callRecordState === 'stopped' && callTranscribeState === 'stopped') {
12781
- return 'RECORDING_AND_TRANSCRIPTION_STOPPED';
12782
- }
12783
- else {
12784
- return 'NO_STATE';
12785
- }
12786
- };
12787
13036
 
12788
13037
  // Copyright (c) Microsoft Corporation.
12789
13038
  // Licensed under the MIT license.
@@ -13192,128 +13441,6 @@ const onFetchCustomButtonPropsTrampoline = (options) => {
13192
13441
  return response;
13193
13442
  };
13194
13443
 
13195
- // Copyright (c) Microsoft Corporation.
13196
- /**
13197
- * @private
13198
- */
13199
- const ComplianceBanner = (props) => {
13200
- function determineStates(previous, current) {
13201
- // if current state is on, then return on
13202
- if (current) {
13203
- return 'on';
13204
- }
13205
- // if current state is off
13206
- else {
13207
- // if previous state is on and current state is off, return stopped (on -> off)
13208
- if (previous === 'on') {
13209
- return 'stopped';
13210
- }
13211
- // otherwise remain previous state unchanged
13212
- else {
13213
- return previous;
13214
- }
13215
- }
13216
- }
13217
- //set variant when incoming state is different from current state
13218
- //when variant change, return message bar
13219
- //when message bar is dismissed,set variant to default nostate and if current state is stopped, set to off
13220
- const strings = useLocale().strings;
13221
- const [variant, setVariant] = React.useState('NO_STATE');
13222
- const cachedProps = React.useRef({
13223
- latestBooleanState: {
13224
- callTranscribeState: false,
13225
- callRecordState: false
13226
- },
13227
- latestStringState: {
13228
- callTranscribeState: 'off',
13229
- callRecordState: 'off'
13230
- }
13231
- });
13232
- // Only update cached props and variant if there is _some_ change in the latest props.
13233
- // This ensures that state machine is only updated if there is an actual change in the props.
13234
- if (props.callRecordState !== cachedProps.current.latestBooleanState.callRecordState ||
13235
- props.callTranscribeState !== cachedProps.current.latestBooleanState.callTranscribeState) {
13236
- cachedProps.current = {
13237
- latestBooleanState: props,
13238
- latestStringState: {
13239
- callRecordState: determineStates(cachedProps.current.latestStringState.callRecordState, props.callRecordState),
13240
- callTranscribeState: determineStates(cachedProps.current.latestStringState.callTranscribeState, props.callTranscribeState)
13241
- }
13242
- };
13243
- setVariant(computeVariant(cachedProps.current.latestStringState.callRecordState, cachedProps.current.latestStringState.callTranscribeState));
13244
- // when both states are stopped, after displaying message "RECORDING_AND_TRANSCRIPTION_STOPPED", change both states to off (going back to the default state)
13245
- if (cachedProps.current.latestStringState.callRecordState === 'stopped' &&
13246
- cachedProps.current.latestStringState.callTranscribeState === 'stopped') {
13247
- cachedProps.current.latestStringState.callRecordState = 'off';
13248
- cachedProps.current.latestStringState.callTranscribeState = 'off';
13249
- }
13250
- }
13251
- return variant === 'NO_STATE' ? (React__default['default'].createElement(React__default['default'].Fragment, null)) : (React__default['default'].createElement(react.MessageBar, { messageBarType: react.MessageBarType.warning, onDismiss: () => {
13252
- // when closing the banner, change variant to nostate and change stopped state to off state.
13253
- // Reason: on banner close, going back to the default state
13254
- setVariant('NO_STATE');
13255
- if (cachedProps.current.latestStringState.callRecordState === 'stopped') {
13256
- cachedProps.current.latestStringState.callRecordState = 'off';
13257
- }
13258
- if (cachedProps.current.latestStringState.callTranscribeState === 'stopped') {
13259
- cachedProps.current.latestStringState.callTranscribeState = 'off';
13260
- }
13261
- }, dismissButtonAriaLabel: strings.call.close },
13262
- React__default['default'].createElement(BannerMessage, { variant: variant, strings: strings })));
13263
- };
13264
- function BannerMessage(props) {
13265
- const { variant, strings } = props;
13266
- switch (variant) {
13267
- case 'TRANSCRIPTION_STOPPED_STILL_RECORDING':
13268
- return (React__default['default'].createElement(React__default['default'].Fragment, null,
13269
- React__default['default'].createElement("b", null, strings.call.complianceBannerTranscriptionStopped),
13270
- ` ${strings.call.complianceBannerNowOnlyRecording}`,
13271
- React__default['default'].createElement(PrivacyPolicy, { linkText: strings.call.privacyPolicy })));
13272
- case 'RECORDING_STOPPED_STILL_TRANSCRIBING':
13273
- return (React__default['default'].createElement(React__default['default'].Fragment, null,
13274
- React__default['default'].createElement("b", null, strings.call.complianceBannerRecordingStopped),
13275
- ` ${strings.call.complianceBannerNowOnlyTranscription}`,
13276
- React__default['default'].createElement(PrivacyPolicy, { linkText: strings.call.privacyPolicy })));
13277
- case 'RECORDING_AND_TRANSCRIPTION_STOPPED':
13278
- return (React__default['default'].createElement(React__default['default'].Fragment, null,
13279
- React__default['default'].createElement("b", null, strings.call.complianceBannerRecordingAndTranscriptionSaved),
13280
- ` ${strings.call.complianceBannerRecordingAndTranscriptionStopped}`,
13281
- React__default['default'].createElement(LearnMore, { linkText: strings.call.learnMore })));
13282
- case 'RECORDING_AND_TRANSCRIPTION_STARTED':
13283
- return (React__default['default'].createElement(React__default['default'].Fragment, null,
13284
- React__default['default'].createElement("b", null, strings.call.complianceBannerRecordingAndTranscriptionStarted),
13285
- ` ${strings.call.complianceBannerTranscriptionConsent}`,
13286
- React__default['default'].createElement(PrivacyPolicy, { linkText: strings.call.privacyPolicy })));
13287
- case 'TRANSCRIPTION_STARTED':
13288
- return (React__default['default'].createElement(React__default['default'].Fragment, null,
13289
- React__default['default'].createElement("b", null, strings.call.complianceBannerTranscriptionStarted),
13290
- ` ${strings.call.complianceBannerTranscriptionConsent}`,
13291
- React__default['default'].createElement(PrivacyPolicy, { linkText: strings.call.privacyPolicy })));
13292
- case 'RECORDING_STOPPED':
13293
- return (React__default['default'].createElement(React__default['default'].Fragment, null,
13294
- React__default['default'].createElement("b", null, strings.call.complianceBannerRecordingSaving),
13295
- ` ${strings.call.complianceBannerRecordingStopped}`,
13296
- React__default['default'].createElement(LearnMore, { linkText: strings.call.learnMore })));
13297
- case 'RECORDING_STARTED':
13298
- return (React__default['default'].createElement(React__default['default'].Fragment, null,
13299
- React__default['default'].createElement("b", null, strings.call.complianceBannerRecordingStarted),
13300
- ` ${strings.call.complianceBannerTranscriptionConsent}`,
13301
- React__default['default'].createElement(PrivacyPolicy, { linkText: strings.call.privacyPolicy })));
13302
- case 'TRANSCRIPTION_STOPPED':
13303
- return (React__default['default'].createElement(React__default['default'].Fragment, null,
13304
- React__default['default'].createElement("b", null, strings.call.complianceBannerTranscriptionSaving),
13305
- ` ${strings.call.complianceBannerTranscriptionStopped}`,
13306
- React__default['default'].createElement(LearnMore, { linkText: strings.call.learnMore })));
13307
- }
13308
- return React__default['default'].createElement(React__default['default'].Fragment, null);
13309
- }
13310
- function PrivacyPolicy(props) {
13311
- return (React__default['default'].createElement(react.Link, { href: "https://privacy.microsoft.com/privacystatement#mainnoticetoendusersmodule", target: "_blank", underline: true }, props.linkText));
13312
- }
13313
- function LearnMore(props) {
13314
- return (React__default['default'].createElement(react.Link, { href: "https://support.microsoft.com/office/record-a-meeting-in-teams-34dfbe7f-b07d-4a27-b4c6-de62f1348c24", target: "_blank", underline: true }, props.linkText));
13315
- }
13316
-
13317
13444
  // Copyright (c) Microsoft Corporation.
13318
13445
  // Licensed under the MIT license.
13319
13446
  /** @private */
@@ -13449,7 +13576,7 @@ const CallArrangement = (props) => {
13449
13576
  React__default['default'].createElement(react.Stack, { verticalFill: true, horizontalAlign: "stretch", className: containerClassName, "data-ui-id": props.dataUiId },
13450
13577
  React__default['default'].createElement(react.Stack.Item, { styles: notificationsContainerStyles },
13451
13578
  React__default['default'].createElement(react.Stack, { styles: bannerNotificationStyles },
13452
- React__default['default'].createElement(ComplianceBanner, Object.assign({}, props.complianceBannerProps))),
13579
+ React__default['default'].createElement(_ComplianceBanner, Object.assign({}, props.complianceBannerProps))),
13453
13580
  props.errorBarProps !== false && (React__default['default'].createElement(react.Stack, { styles: bannerNotificationStyles },
13454
13581
  React__default['default'].createElement(ErrorBar, Object.assign({}, props.errorBarProps)))),
13455
13582
  !!props.mutedNotificationProps && React__default['default'].createElement(MutedNotification, Object.assign({}, props.mutedNotificationProps))),
@@ -13777,9 +13904,10 @@ const CallPage = (props) => {
13777
13904
  const errorBarProps = usePropsFor$1(ErrorBar);
13778
13905
  const mutedNotificationProps = useSelector$1(mutedNotificationSelector);
13779
13906
  const networkReconnectTileProps = useSelector$1(networkReconnectTileSelector);
13907
+ const strings = useLocale().strings.call;
13780
13908
  // Reduce the controls shown when mobile view is enabled.
13781
13909
  const callControlOptions = mobileView ? reduceCallControlsForMobile(options === null || options === void 0 ? void 0 : options.callControls) : options === null || options === void 0 ? void 0 : options.callControls;
13782
- return (React__default['default'].createElement(CallArrangement, { complianceBannerProps: Object.assign({}, complianceBannerProps), errorBarProps: (options === null || options === void 0 ? void 0 : options.errorBar) !== false && Object.assign({}, errorBarProps), mutedNotificationProps: mutedNotificationProps, callControlProps: {
13910
+ return (React__default['default'].createElement(CallArrangement, { complianceBannerProps: Object.assign(Object.assign({}, complianceBannerProps), { strings }), errorBarProps: (options === null || options === void 0 ? void 0 : options.errorBar) !== false && Object.assign({}, errorBarProps), mutedNotificationProps: mutedNotificationProps, callControlProps: {
13783
13911
  callInvitationURL: callInvitationURL,
13784
13912
  onFetchParticipantMenuItems: onFetchParticipantMenuItems,
13785
13913
  options: callControlOptions,
@@ -14333,7 +14461,7 @@ const LobbyPage = (props) => {
14333
14461
  ? reduceCallControlsForMobile((_a = props.options) === null || _a === void 0 ? void 0 : _a.callControls)
14334
14462
  : (_b = props.options) === null || _b === void 0 ? void 0 : _b.callControls;
14335
14463
  callControlOptions = disableLobbyPageControls(callControlOptions);
14336
- return (React__default['default'].createElement(CallArrangement, { complianceBannerProps: {}, errorBarProps: ((_c = props.options) === null || _c === void 0 ? void 0 : _c.errorBar) !== false && Object.assign({}, errorBarProps), callControlProps: {
14464
+ return (React__default['default'].createElement(CallArrangement, { complianceBannerProps: { strings }, errorBarProps: ((_c = props.options) === null || _c === void 0 ? void 0 : _c.errorBar) !== false && Object.assign({}, errorBarProps), callControlProps: {
14337
14465
  options: callControlOptions,
14338
14466
  increaseFlyoutItemSize: props.mobileView
14339
14467
  }, mobileView: props.mobileView, onRenderGalleryContent: () => React__default['default'].createElement(LobbyTile, Object.assign({}, lobbyProps, { overlayProps: overlayProps(strings, inLobby) })), dataUiId: 'lobby-page' }));
@@ -16307,7 +16435,7 @@ const CallWithChatScreen = (props) => {
16307
16435
  setActivePane('none');
16308
16436
  }, [setActivePane]);
16309
16437
  /** Constant setting of id for the parent stack of the composite */
16310
- const compositeParentDivId = 'callWithChatCompositeParentDiv-internal';
16438
+ const compositeParentDivId = reactHooks.useId('callWithChatCompositeParentDiv-internal');
16311
16439
  const toggleChat = React.useCallback(() => {
16312
16440
  if (activePane === 'chat') {
16313
16441
  setActivePane('none');