stream-chat-react 14.0.0 → 14.1.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 (165) hide show
  1. package/dist/cjs/emojis.js +8 -8
  2. package/dist/cjs/emojis.js.map +1 -1
  3. package/dist/cjs/index.js +18362 -16461
  4. package/dist/cjs/index.js.map +1 -1
  5. package/dist/cjs/{WithAudioPlayback.d26afa91.js → useNotificationApi.fd802923.js} +1439 -1360
  6. package/dist/cjs/useNotificationApi.fd802923.js.map +1 -0
  7. package/dist/css/emoji-picker.css +2 -3
  8. package/dist/css/emoji-picker.css.map +1 -1
  9. package/dist/css/index.css +2491 -3685
  10. package/dist/css/index.css.map +1 -1
  11. package/dist/es/emojis.mjs +1 -1
  12. package/dist/es/index.mjs +18111 -16210
  13. package/dist/es/index.mjs.map +1 -1
  14. package/dist/es/{WithAudioPlayback.9b779236.mjs → useNotificationApi.e0c52de6.mjs} +1573 -1494
  15. package/dist/es/useNotificationApi.e0c52de6.mjs.map +1 -0
  16. package/dist/types/a11y/a11yUtils.d.ts +23 -0
  17. package/dist/types/a11y/a11yUtils.d.ts.map +1 -0
  18. package/dist/types/a11y/hooks/useAriaIdentifiers.d.ts +15 -0
  19. package/dist/types/a11y/hooks/useAriaIdentifiers.d.ts.map +1 -0
  20. package/dist/types/a11y/hooks/useResolvedModalAriaProps.d.ts +22 -0
  21. package/dist/types/a11y/hooks/useResolvedModalAriaProps.d.ts.map +1 -0
  22. package/dist/types/components/Accessibility/AriaLiveRegion.d.ts +3 -0
  23. package/dist/types/components/Accessibility/AriaLiveRegion.d.ts.map +1 -0
  24. package/dist/types/components/Accessibility/NotificationAnnouncer.d.ts +14 -0
  25. package/dist/types/components/Accessibility/NotificationAnnouncer.d.ts.map +1 -0
  26. package/dist/types/components/Accessibility/hooks/useIncomingMessageAnnouncements.d.ts +9 -0
  27. package/dist/types/components/Accessibility/hooks/useIncomingMessageAnnouncements.d.ts.map +1 -0
  28. package/dist/types/components/Accessibility/index.d.ts +5 -0
  29. package/dist/types/components/Accessibility/index.d.ts.map +1 -0
  30. package/dist/types/components/Accessibility/useAriaLiveAnnouncer.d.ts +9 -0
  31. package/dist/types/components/Accessibility/useAriaLiveAnnouncer.d.ts.map +1 -0
  32. package/dist/types/components/Attachment/Audio.d.ts.map +1 -1
  33. package/dist/types/components/Attachment/LinkPreview/CardAudio.d.ts.map +1 -1
  34. package/dist/types/components/Attachment/ModalGallery.d.ts.map +1 -1
  35. package/dist/types/components/Attachment/VoiceRecording.d.ts.map +1 -1
  36. package/dist/types/components/AudioPlayback/components/DurationDisplay.d.ts.map +1 -1
  37. package/dist/types/components/AudioPlayback/components/ProgressBar.d.ts +5 -1
  38. package/dist/types/components/AudioPlayback/components/ProgressBar.d.ts.map +1 -1
  39. package/dist/types/components/AudioPlayback/components/WaveProgressBar.d.ts +5 -1
  40. package/dist/types/components/AudioPlayback/components/WaveProgressBar.d.ts.map +1 -1
  41. package/dist/types/components/AudioPlayback/components/formatTime.d.ts +2 -0
  42. package/dist/types/components/AudioPlayback/components/formatTime.d.ts.map +1 -0
  43. package/dist/types/components/AudioPlayback/components/keyboardSeek.d.ts +12 -0
  44. package/dist/types/components/AudioPlayback/components/keyboardSeek.d.ts.map +1 -0
  45. package/dist/types/components/AudioPlayback/components/progressBarA11y.d.ts +10 -0
  46. package/dist/types/components/AudioPlayback/components/progressBarA11y.d.ts.map +1 -0
  47. package/dist/types/components/Avatar/Avatar.d.ts +1 -1
  48. package/dist/types/components/Avatar/Avatar.d.ts.map +1 -1
  49. package/dist/types/components/Button/PlayButton.d.ts.map +1 -1
  50. package/dist/types/components/ChannelListItem/ChannelListItemActionButtons.d.ts.map +1 -1
  51. package/dist/types/components/ChannelListItem/ChannelListItemActionButtons.defaults.d.ts.map +1 -1
  52. package/dist/types/components/ChannelListItem/ChannelListItemUI.d.ts.map +1 -1
  53. package/dist/types/components/Chat/Chat.d.ts.map +1 -1
  54. package/dist/types/components/ChatView/ChatView.a11y.utility.d.ts +26 -0
  55. package/dist/types/components/ChatView/ChatView.a11y.utility.d.ts.map +1 -0
  56. package/dist/types/components/ChatView/ChatView.d.ts +9 -0
  57. package/dist/types/components/ChatView/ChatView.d.ts.map +1 -1
  58. package/dist/types/components/Dialog/components/Alert.d.ts +3 -1
  59. package/dist/types/components/Dialog/components/Alert.d.ts.map +1 -1
  60. package/dist/types/components/Dialog/components/ContextMenu.d.ts +31 -6
  61. package/dist/types/components/Dialog/components/ContextMenu.d.ts.map +1 -1
  62. package/dist/types/components/Dialog/components/Prompt.d.ts +3 -1
  63. package/dist/types/components/Dialog/components/Prompt.d.ts.map +1 -1
  64. package/dist/types/components/Dialog/components/Viewer.d.ts +3 -1
  65. package/dist/types/components/Dialog/components/Viewer.d.ts.map +1 -1
  66. package/dist/types/components/Dialog/service/DialogAnchor.d.ts.map +1 -1
  67. package/dist/types/components/Form/Dropdown.d.ts.map +1 -1
  68. package/dist/types/components/Form/NumericInput.d.ts.map +1 -1
  69. package/dist/types/components/Form/SwitchField.d.ts +5 -2
  70. package/dist/types/components/Form/SwitchField.d.ts.map +1 -1
  71. package/dist/types/components/Form/TextInput.d.ts.map +1 -1
  72. package/dist/types/components/Icons/BaseIcon.d.ts +4 -1
  73. package/dist/types/components/Icons/BaseIcon.d.ts.map +1 -1
  74. package/dist/types/components/Icons/createIcon.d.ts +6 -3
  75. package/dist/types/components/Icons/createIcon.d.ts.map +1 -1
  76. package/dist/types/components/Icons/icons.d.ts +81 -82
  77. package/dist/types/components/Icons/icons.d.ts.map +1 -1
  78. package/dist/types/components/InfiniteScrollPaginator/InfiniteScroll.d.ts.map +1 -1
  79. package/dist/types/components/Loading/LoadingIndicator.d.ts +2 -2
  80. package/dist/types/components/Loading/LoadingIndicator.d.ts.map +1 -1
  81. package/dist/types/components/Location/ShareLocationDialog.d.ts +1 -0
  82. package/dist/types/components/Location/ShareLocationDialog.d.ts.map +1 -1
  83. package/dist/types/components/MediaRecorder/AudioRecorder/AudioRecorderRecordingControls.d.ts.map +1 -1
  84. package/dist/types/components/MediaRecorder/AudioRecorder/AudioRecordingButtonWithNotification.d.ts.map +1 -1
  85. package/dist/types/components/MediaRecorder/AudioRecorder/AudioRecordingPlayback.d.ts.map +1 -1
  86. package/dist/types/components/Message/Message.d.ts.map +1 -1
  87. package/dist/types/components/Message/MessageText.d.ts.map +1 -1
  88. package/dist/types/components/Message/MessageUI.d.ts.map +1 -1
  89. package/dist/types/components/Message/types.d.ts +4 -2
  90. package/dist/types/components/Message/types.d.ts.map +1 -1
  91. package/dist/types/components/Message/utils.d.ts +1 -1
  92. package/dist/types/components/Message/utils.d.ts.map +1 -1
  93. package/dist/types/components/MessageActions/DeleteMessageAlert.d.ts.map +1 -1
  94. package/dist/types/components/MessageActions/MessageActions.d.ts.map +1 -1
  95. package/dist/types/components/MessageActions/MessageActions.defaults.d.ts.map +1 -1
  96. package/dist/types/components/MessageBounce/MessageBouncePrompt.d.ts.map +1 -1
  97. package/dist/types/components/MessageComposer/AttachmentPreviewList/AttachmentPreviewList.d.ts.map +1 -1
  98. package/dist/types/components/MessageComposer/AttachmentPreviewList/AudioAttachmentPreview.d.ts.map +1 -1
  99. package/dist/types/components/MessageComposer/AttachmentPreviewList/utils/AttachmentPreviewRoot.d.ts.map +1 -1
  100. package/dist/types/components/MessageComposer/AttachmentSelector/AttachmentSelector.d.ts.map +1 -1
  101. package/dist/types/components/MessageComposer/CommandChip.d.ts.map +1 -1
  102. package/dist/types/components/MessageComposer/QuotedMessagePreview.d.ts.map +1 -1
  103. package/dist/types/components/MessageList/MessageList.d.ts.map +1 -1
  104. package/dist/types/components/MessageList/NewMessageNotification.d.ts.map +1 -1
  105. package/dist/types/components/MessageList/ScrollToLatestMessageButton.d.ts +1 -2
  106. package/dist/types/components/MessageList/ScrollToLatestMessageButton.d.ts.map +1 -1
  107. package/dist/types/components/MessageList/UnreadMessagesNotification.d.ts.map +1 -1
  108. package/dist/types/components/MessageList/UnreadMessagesSeparator.d.ts.map +1 -1
  109. package/dist/types/components/MessageList/VirtualizedMessageList.d.ts +1 -1
  110. package/dist/types/components/MessageList/VirtualizedMessageList.d.ts.map +1 -1
  111. package/dist/types/components/MessageList/hooks/MessageList/useMessageListScrollManager.d.ts +1 -0
  112. package/dist/types/components/MessageList/hooks/MessageList/useMessageListScrollManager.d.ts.map +1 -1
  113. package/dist/types/components/MessageList/hooks/MessageList/useScrollLocationLogic.d.ts.map +1 -1
  114. package/dist/types/components/MessageList/hooks/useReducedMotionPreference.d.ts +2 -0
  115. package/dist/types/components/MessageList/hooks/useReducedMotionPreference.d.ts.map +1 -0
  116. package/dist/types/components/Modal/CloseButtonOnModalOverlay.d.ts.map +1 -1
  117. package/dist/types/components/Modal/GlobalModal.d.ts +9 -1
  118. package/dist/types/components/Modal/GlobalModal.d.ts.map +1 -1
  119. package/dist/types/components/Notifications/Notification.d.ts.map +1 -1
  120. package/dist/types/components/Notifications/NotificationList.d.ts.map +1 -1
  121. package/dist/types/components/Poll/PollActions/AddCommentPrompt.d.ts.map +1 -1
  122. package/dist/types/components/Poll/PollActions/PollAnswerList.d.ts.map +1 -1
  123. package/dist/types/components/Poll/PollActions/PollOptionsFullList.d.ts.map +1 -1
  124. package/dist/types/components/Poll/PollActions/PollResults/PollResults.d.ts.map +1 -1
  125. package/dist/types/components/Poll/PollActions/SuggestPollOptionPrompt.d.ts.map +1 -1
  126. package/dist/types/components/Poll/PollCreationDialog/MultipleAnswersField.d.ts.map +1 -1
  127. package/dist/types/components/Poll/PollCreationDialog/NameField.d.ts.map +1 -1
  128. package/dist/types/components/Poll/PollCreationDialog/OptionFieldSet.d.ts.map +1 -1
  129. package/dist/types/components/Poll/PollCreationDialog/PollCreationDialog.d.ts.map +1 -1
  130. package/dist/types/components/Poll/PollOptionSelector.d.ts.map +1 -1
  131. package/dist/types/components/Reactions/MessageReactions.d.ts.map +1 -1
  132. package/dist/types/components/Reactions/MessageReactionsDetail.d.ts.map +1 -1
  133. package/dist/types/components/Reactions/ReactionSelector.d.ts.map +1 -1
  134. package/dist/types/components/Reactions/ReactionSelectorWithButton.d.ts.map +1 -1
  135. package/dist/types/components/Search/SearchBar/SearchBar.d.ts.map +1 -1
  136. package/dist/types/components/Search/SearchResults/SearchResultItem.d.ts.map +1 -1
  137. package/dist/types/components/Search/SearchResults/SearchResultsHeader.d.ts.map +1 -1
  138. package/dist/types/components/SkipNavigation/SkipNavigation.d.ts +38 -0
  139. package/dist/types/components/SkipNavigation/SkipNavigation.d.ts.map +1 -0
  140. package/dist/types/components/SkipNavigation/index.d.ts +2 -0
  141. package/dist/types/components/SkipNavigation/index.d.ts.map +1 -0
  142. package/dist/types/components/TextareaComposer/SuggestionList/SuggestionList.d.ts.map +1 -1
  143. package/dist/types/components/Thread/ThreadHeader.d.ts.map +1 -1
  144. package/dist/types/components/Threads/ThreadList/ThreadList.d.ts.map +1 -1
  145. package/dist/types/components/TypingIndicator/TypingIndicator.d.ts.map +1 -1
  146. package/dist/types/components/TypingIndicator/TypingIndicatorHeader.d.ts.map +1 -1
  147. package/dist/types/components/TypingIndicator/utils/getTypingStatusMessage.d.ts +8 -0
  148. package/dist/types/components/TypingIndicator/utils/getTypingStatusMessage.d.ts.map +1 -0
  149. package/dist/types/components/VisuallyHidden/VisuallyHidden.d.ts +7 -0
  150. package/dist/types/components/VisuallyHidden/VisuallyHidden.d.ts.map +1 -0
  151. package/dist/types/components/VisuallyHidden/index.d.ts +2 -0
  152. package/dist/types/components/VisuallyHidden/index.d.ts.map +1 -0
  153. package/dist/types/components/index.d.ts +3 -0
  154. package/dist/types/components/index.d.ts.map +1 -1
  155. package/dist/types/context/ComponentContext.d.ts +10 -1
  156. package/dist/types/context/ComponentContext.d.ts.map +1 -1
  157. package/dist/types/context/ModalContext.d.ts +1 -0
  158. package/dist/types/context/ModalContext.d.ts.map +1 -1
  159. package/dist/types/context/index.d.ts +1 -0
  160. package/dist/types/context/index.d.ts.map +1 -1
  161. package/dist/types/i18n/Streami18n.d.ts +55 -2
  162. package/dist/types/i18n/Streami18n.d.ts.map +1 -1
  163. package/package.json +2 -8
  164. package/dist/cjs/WithAudioPlayback.d26afa91.js.map +0 -1
  165. package/dist/es/WithAudioPlayback.9b779236.mjs.map +0 -1
@@ -1,13 +1,13 @@
1
1
  import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
- import React, { useContext, createContext, useCallback, useMemo, forwardRef, useRef, useEffect, createElement, useState, useReducer, useLayoutEffect } from "react";
2
+ import React, { useContext, createContext, useCallback, useMemo, forwardRef, useRef, useEffect, useState, createElement, useReducer, useLayoutEffect, useId } from "react";
3
3
  import { flip, shift, size, useFloating, autoUpdate, autoPlacement, offset } from "@floating-ui/react";
4
4
  import { StateStore, MessageComposer, FixedSizeQueueCache, ErrorFromResponse, localMessageToNewMessagePayload } from "stream-chat";
5
- import throttle from "lodash.throttle";
6
- import { useSyncExternalStore } from "use-sync-external-store/shim";
7
5
  import clsx from "clsx";
8
6
  import debounce from "lodash.debounce";
7
+ import throttle from "lodash.throttle";
9
8
  import Dayjs from "dayjs";
10
9
  import * as linkify from "linkifyjs";
10
+ import { useSyncExternalStore } from "use-sync-external-store/shim";
11
11
  import { nanoid } from "nanoid";
12
12
  import deepequal from "react-fast-compare";
13
13
  import emojiRegex from "emoji-regex";
@@ -372,15 +372,96 @@ const useTypingContext = (componentName) => {
372
372
  }
373
373
  return contextValue;
374
374
  };
375
- const BaseIcon = ({ className, ...props }) => /* @__PURE__ */ jsx(
376
- "svg",
377
- {
378
- viewBox: "0 0 20 20",
379
- xmlns: "http://www.w3.org/2000/svg",
380
- ...props,
381
- className: clsx("str-chat__icon", className)
375
+ const NOTIFICATION_TARGET_PANELS = [
376
+ "channel",
377
+ "thread",
378
+ "channel-list",
379
+ "thread-list"
380
+ ];
381
+ const isNotificationTargetPanel = (value) => typeof value === "string" && NOTIFICATION_TARGET_PANELS.includes(value);
382
+ const getNotificationTargetPanel = (notification) => {
383
+ const targetTag = notification.tags?.find((tag) => tag.startsWith("target:"));
384
+ if (targetTag) {
385
+ const candidate = targetTag.slice("target:".length);
386
+ if (isNotificationTargetPanel(candidate)) return candidate;
382
387
  }
383
- );
388
+ const panel = notification.origin.context?.panel;
389
+ return isNotificationTargetPanel(panel) ? panel : void 0;
390
+ };
391
+ const getNotificationTargetPanels = (notification) => {
392
+ const targetPanels = (notification.tags ?? []).filter((tag) => tag.startsWith("target:")).map((tag) => tag.slice("target:".length)).filter(
393
+ (value) => isNotificationTargetPanel(value)
394
+ );
395
+ if (targetPanels.length > 0) {
396
+ return Array.from(new Set(targetPanels));
397
+ }
398
+ const panel = notification.origin.context?.panel;
399
+ return isNotificationTargetPanel(panel) ? [panel] : [];
400
+ };
401
+ const getNotificationTargetTag = (panel) => `target:${panel}`;
402
+ const addNotificationTargetTag = (panel, tags) => {
403
+ if (!panel) return tags ?? [];
404
+ return Array.from(/* @__PURE__ */ new Set([getNotificationTargetTag(panel), ...tags ?? []]));
405
+ };
406
+ const isNotificationForPanel = (notification, panel, options) => {
407
+ const explicitTargetPanels = getNotificationTargetPanels(notification);
408
+ if (explicitTargetPanels.length > 0) {
409
+ return explicitTargetPanels.includes(panel);
410
+ }
411
+ const resolvedPanel = options?.fallbackPanel ?? "channel";
412
+ return resolvedPanel === panel;
413
+ };
414
+ const variantToClass = {
415
+ danger: "str-chat__button--destructive",
416
+ primary: "str-chat__button--primary",
417
+ secondary: "str-chat__button--secondary"
418
+ };
419
+ const appearanceToClass = {
420
+ ghost: "str-chat__button--ghost",
421
+ outline: "str-chat__button--outline",
422
+ solid: "str-chat__button--solid"
423
+ };
424
+ const sizeToClass = {
425
+ lg: "str-chat__button--size-lg",
426
+ md: "str-chat__button--size-md",
427
+ sm: "str-chat__button--size-sm",
428
+ xs: "str-chat__button--size-xs"
429
+ };
430
+ const Button = forwardRef(function Button2({ appearance, children, circular, className, inverseTheme, size: size2, variant, ...props }, ref) {
431
+ return /* @__PURE__ */ jsx(
432
+ "button",
433
+ {
434
+ ref,
435
+ type: "button",
436
+ ...props,
437
+ className: clsx(
438
+ "str-chat__button",
439
+ variant != null && variantToClass[variant],
440
+ appearance != null && appearanceToClass[appearance],
441
+ circular && "str-chat__button--circular",
442
+ inverseTheme && "str-chat__theme-inverse",
443
+ size2 != null && sizeToClass[size2],
444
+ className
445
+ ),
446
+ children: /* @__PURE__ */ jsx("div", { className: "str-chat__button__content", children })
447
+ }
448
+ );
449
+ });
450
+ const BaseIcon = ({ className, decorative = true, ...props }) => {
451
+ const ariaHidden = props["aria-hidden"] ?? (decorative ? true : void 0);
452
+ const focusable = props.focusable ?? (decorative ? false : void 0);
453
+ return /* @__PURE__ */ jsx(
454
+ "svg",
455
+ {
456
+ viewBox: "0 0 20 20",
457
+ xmlns: "http://www.w3.org/2000/svg",
458
+ ...props,
459
+ "aria-hidden": ariaHidden,
460
+ className: clsx("str-chat__icon", className),
461
+ focusable
462
+ }
463
+ );
464
+ };
384
465
  function toIconClass(name) {
385
466
  return "str-chat__icon--" + name.replace(/^Icon/, "").replace(/([a-z])([A-Z])/g, "$1-$2").replace(/([A-Za-z])(\d)/g, "$1-$2").replace(/(\d)([A-Za-z])/g, "$1-$2").replace(/_/g, "-").toLowerCase();
386
467
  }
@@ -1495,977 +1576,259 @@ const IconGiphy = createIcon(
1495
1576
  )
1496
1577
  ] })
1497
1578
  );
1498
- const DEFAULT_PLAYBACK_RATES = [1, 1.5, 2];
1499
- const isSeekable = (audioElement) => !(audioElement.duration === Infinity || isNaN(audioElement.duration));
1500
- const defaultRegisterAudioPlayerError = ({
1501
- error
1502
- } = {}) => {
1503
- if (!error) return;
1504
- console.error("[AUDIO PLAYER]", error);
1579
+ const UnMemoizedEmptyStateIndicator = (props) => {
1580
+ const { listType, messageText } = props;
1581
+ const { t } = useTranslationContext("EmptyStateIndicator");
1582
+ if (listType === "thread") return null;
1583
+ if (listType === "channel") {
1584
+ const text = t("No conversations yet");
1585
+ return /* @__PURE__ */ jsxs("div", { className: "str-chat__channel-list-empty", children: [
1586
+ /* @__PURE__ */ jsx(IconMessageBubbles, {}),
1587
+ /* @__PURE__ */ jsx("p", { role: "listitem", children: text })
1588
+ ] });
1589
+ }
1590
+ if (listType === "message") {
1591
+ const text = t(messageText || "Send a message to start the conversation");
1592
+ return /* @__PURE__ */ jsxs("div", { className: "str-chat__empty-channel", children: [
1593
+ /* @__PURE__ */ jsx(IconMessageBubble, {}),
1594
+ /* @__PURE__ */ jsx("p", { className: "str-chat__empty-channel-text", role: "listitem", children: text })
1595
+ ] });
1596
+ }
1597
+ return /* @__PURE__ */ jsx("p", { children: t("No items exist") });
1505
1598
  };
1506
- const elementIsPlaying = (audioElement) => audioElement && !(audioElement.paused || audioElement.ended);
1507
- class AudioPlayer {
1508
- constructor({
1509
- durationSeconds,
1510
- fileSize,
1511
- id,
1512
- mimeType,
1513
- playbackRates: customPlaybackRates,
1514
- plugins,
1515
- pool,
1516
- src,
1517
- title,
1518
- waveformData
1519
- }) {
1520
- this._plugins = /* @__PURE__ */ new Map();
1521
- this.playTimeout = void 0;
1522
- this.unsubscribeEventListeners = null;
1523
- this._disposed = false;
1524
- this._metadataProbe = null;
1525
- this._restoringPosition = false;
1526
- this._removalTimeout = void 0;
1527
- this.setDurationSeconds = (durationSeconds2) => {
1528
- this._data.durationSeconds = durationSeconds2;
1529
- this.state.partialNext({ durationSeconds: durationSeconds2 });
1530
- };
1531
- this.setPlaybackStartSafetyTimeout = () => {
1532
- clearTimeout(this.playTimeout);
1533
- this.playTimeout = setTimeout(() => {
1534
- if (!this.elementRef) return;
1535
- try {
1536
- this.elementRef.pause();
1537
- this.state.partialNext({ isPlaying: false });
1538
- } catch (e) {
1539
- this.registerError({ errCode: "failed-to-start" });
1540
- }
1541
- }, 2e3);
1542
- };
1543
- this.updateDurationFromElement = (element) => {
1544
- const duration = element.duration;
1545
- if (typeof duration !== "number" || isNaN(duration) || !isFinite(duration) || duration <= 0) {
1546
- return;
1547
- }
1548
- this.setDurationSeconds(duration);
1549
- };
1550
- this.clearMetadataProbe = () => {
1551
- const probe = this._metadataProbe;
1552
- this._metadataProbe = null;
1553
- this._metadataProbePromise = void 0;
1554
- if (!probe) return;
1555
- try {
1556
- probe.pause();
1557
- } catch {
1558
- }
1559
- probe.removeAttribute("src");
1560
- try {
1561
- probe.load();
1562
- } catch {
1563
- }
1564
- };
1565
- this.preloadMetadata = () => {
1566
- if (this._disposed || this.durationSeconds != null || !this.src || this._metadataProbePromise || typeof document === "undefined") {
1567
- return;
1568
- }
1569
- const probe = document.createElement("audio");
1570
- probe.preload = "metadata";
1571
- this._metadataProbe = probe;
1572
- this._metadataProbePromise = new Promise((resolve) => {
1573
- const cleanup = () => {
1574
- probe.removeEventListener("loadedmetadata", handleLoadedMetadata);
1575
- probe.removeEventListener("error", handleError);
1576
- if (this._metadataProbe === probe) {
1577
- this.clearMetadataProbe();
1578
- } else {
1579
- this._metadataProbePromise = void 0;
1580
- }
1581
- resolve();
1582
- };
1583
- const handleLoadedMetadata = () => {
1584
- this.updateDurationFromElement(probe);
1585
- cleanup();
1586
- };
1587
- const handleError = () => {
1588
- cleanup();
1589
- };
1590
- probe.addEventListener("loadedmetadata", handleLoadedMetadata, { once: true });
1591
- probe.addEventListener("error", handleError, { once: true });
1592
- probe.src = this.src;
1593
- try {
1594
- probe.load();
1595
- } catch {
1596
- cleanup();
1597
- }
1598
- });
1599
- };
1600
- this.clearPlaybackStartSafetyTimeout = () => {
1601
- if (!this.elementRef) return;
1602
- clearTimeout(this.playTimeout);
1603
- this.playTimeout = void 0;
1604
- };
1605
- this.clearPendingLoadedMeta = () => {
1606
- const pending = this._pendingLoadedMeta;
1607
- if (pending?.element && pending.onLoaded) {
1608
- pending.element.removeEventListener("loadedmetadata", pending.onLoaded);
1609
- }
1610
- this._pendingLoadedMeta = void 0;
1611
- };
1612
- this.restoreSavedPosition = (elementRef) => {
1613
- const saved = this.secondsElapsed;
1614
- if (!saved || saved <= 0) return;
1615
- const apply = () => {
1616
- const duration = elementRef.duration;
1617
- const clamped = typeof duration === "number" && !isNaN(duration) && isFinite(duration) ? Math.min(saved, duration) : saved;
1618
- try {
1619
- if (elementRef.currentTime === clamped) return;
1620
- elementRef.currentTime = clamped;
1621
- this.setSecondsElapsed(clamped);
1622
- } catch {
1623
- }
1599
+ const EmptyStateIndicator = React.memo(
1600
+ UnMemoizedEmptyStateIndicator
1601
+ );
1602
+ const makeChannelReducer = () => (state, action) => {
1603
+ switch (action.type) {
1604
+ case "closeThread": {
1605
+ return {
1606
+ ...state,
1607
+ thread: null,
1608
+ threadLoadingMore: false,
1609
+ threadMessages: []
1624
1610
  };
1625
- if (elementRef.readyState < 1) {
1626
- this.clearPendingLoadedMeta();
1627
- this._restoringPosition = true;
1628
- const onLoaded = () => {
1629
- if (this._pendingLoadedMeta?.onLoaded !== onLoaded) return;
1630
- this._pendingLoadedMeta = void 0;
1631
- if (this.elementRef !== elementRef) {
1632
- this._restoringPosition = false;
1633
- return;
1634
- }
1635
- apply();
1636
- this._restoringPosition = false;
1637
- };
1638
- elementRef.addEventListener("loadedmetadata", onLoaded, { once: true });
1639
- this._pendingLoadedMeta = { element: elementRef, onLoaded };
1640
- } else {
1641
- this._restoringPosition = true;
1642
- apply();
1643
- this._restoringPosition = false;
1644
- }
1645
- };
1646
- this.elementIsReady = () => {
1647
- if (this._elementIsReadyPromise) return this._elementIsReadyPromise;
1648
- this._elementIsReadyPromise = new Promise((resolve) => {
1649
- if (!this.elementRef) return resolve(false);
1650
- const element = this.elementRef;
1651
- const handleLoaded = () => {
1652
- element.removeEventListener("loadedmetadata", handleLoaded);
1653
- resolve(element.readyState > 0);
1654
- };
1655
- element.addEventListener("loadedmetadata", handleLoaded);
1656
- });
1657
- return this._elementIsReadyPromise;
1658
- };
1659
- this.setRef = (elementRef) => {
1660
- if (elementIsPlaying(this.elementRef)) {
1661
- this.releaseElement({ resetState: false });
1662
- }
1663
- this.clearPendingLoadedMeta();
1664
- this.clearMetadataProbe();
1665
- this._restoringPosition = false;
1666
- this._elementIsReadyPromise = void 0;
1667
- this.state.partialNext({ elementRef });
1668
- if (elementRef) {
1669
- this.registerSubscriptions();
1670
- }
1671
- };
1672
- this.setSecondsElapsed = (secondsElapsed) => {
1673
- const duration = this.elementRef?.duration ?? this.durationSeconds;
1674
- this.state.partialNext({
1675
- progressPercent: duration && secondsElapsed ? secondsElapsed / duration * 100 : 0,
1676
- secondsElapsed
1677
- });
1678
- };
1679
- this.canPlayMimeType = (mimeType2) => {
1680
- if (!mimeType2) return false;
1681
- if (this.elementRef) return !!this.elementRef.canPlayType(mimeType2);
1682
- return !!new Audio().canPlayType(mimeType2);
1683
- };
1684
- this.play = async (params) => {
1685
- if (this._disposed) return;
1686
- const elementRef = this.ensureElementRef();
1687
- if (elementIsPlaying(this.elementRef)) {
1688
- if (this.isPlaying) return;
1689
- this.state.partialNext({ isPlaying: true });
1690
- return;
1691
- }
1692
- const { currentPlaybackRate, playbackRates: playbackRates2 } = {
1693
- currentPlaybackRate: this.currentPlaybackRate,
1694
- playbackRates: this.playbackRates,
1695
- ...params
1611
+ }
1612
+ case "copyMessagesFromChannel": {
1613
+ const { channel, parentId } = action;
1614
+ return {
1615
+ ...state,
1616
+ messages: [...channel.state.messages],
1617
+ pinnedMessages: [...channel.state.pinnedMessages],
1618
+ // copying messages from channel happens with new message - this resets the suppressAutoscroll
1619
+ suppressAutoscroll: false,
1620
+ threadMessages: parentId ? { ...channel.state.threads }[parentId] || [] : state.threadMessages
1696
1621
  };
1697
- if (!this.canPlayRecord) {
1698
- this.registerError({ errCode: "not-playable" });
1699
- return;
1700
- }
1701
- this.restoreSavedPosition(elementRef);
1702
- elementRef.playbackRate = currentPlaybackRate ?? this.currentPlaybackRate;
1703
- this.setPlaybackStartSafetyTimeout();
1704
- try {
1705
- await elementRef.play();
1706
- this.state.partialNext({
1707
- currentPlaybackRate,
1708
- isPlaying: true,
1709
- playbackRates: playbackRates2
1710
- });
1711
- this._pool.setActiveAudioPlayer(this);
1712
- } catch (e) {
1713
- this.registerError({ error: e });
1714
- this.state.partialNext({ isPlaying: false });
1715
- } finally {
1716
- this.clearPlaybackStartSafetyTimeout();
1717
- }
1718
- };
1719
- this.pause = () => {
1720
- if (!elementIsPlaying(this.elementRef)) return;
1721
- this.clearPlaybackStartSafetyTimeout();
1722
- this.elementRef.pause();
1723
- this.state.partialNext({ isPlaying: false });
1724
- };
1725
- this.stop = () => {
1726
- this.pause();
1727
- this.state.partialNext({ isPlaying: false });
1728
- this.setSecondsElapsed(0);
1729
- if (this.elementRef) this.elementRef.currentTime = 0;
1730
- };
1731
- this.togglePlay = async () => this.isPlaying ? this.pause() : await this.play();
1732
- this.increasePlaybackRate = () => {
1733
- let currentPlaybackRateIndex = this.state.getLatestValue().playbackRates.findIndex((rate) => rate === this.currentPlaybackRate);
1734
- if (currentPlaybackRateIndex === -1) {
1735
- currentPlaybackRateIndex = 0;
1736
- }
1737
- const nextIndex = currentPlaybackRateIndex === this.playbackRates.length - 1 ? 0 : currentPlaybackRateIndex + 1;
1738
- const currentPlaybackRate = this.playbackRates[nextIndex];
1739
- this.state.partialNext({ currentPlaybackRate });
1740
- if (this.elementRef) {
1741
- this.elementRef.playbackRate = currentPlaybackRate;
1742
- }
1743
- };
1744
- this.seek = throttle(async ({ clientX, currentTarget }) => {
1745
- let element = this.elementRef;
1746
- if (!this.elementRef) {
1747
- element = this.ensureElementRef();
1748
- const isReady = await this.elementIsReady();
1749
- if (!isReady) return;
1750
- }
1751
- if (!currentTarget || !element) return;
1752
- if (!isSeekable(element)) {
1753
- this.registerError({ errCode: "seek-not-supported" });
1754
- return;
1755
- }
1756
- const { width, x } = currentTarget.getBoundingClientRect();
1757
- const ratio = (clientX - x) / width;
1758
- if (ratio > 1 || ratio < 0) return;
1759
- const currentTime = ratio * element.duration;
1760
- this.setSecondsElapsed(currentTime);
1761
- element.currentTime = currentTime;
1762
- }, 16);
1763
- this.registerError = (params) => {
1764
- defaultRegisterAudioPlayerError(params);
1765
- this.plugins.forEach(({ onError }) => onError?.({ player: this, ...params }));
1766
- };
1767
- this.requestRemoval = () => {
1768
- this._disposed = true;
1769
- this.cancelScheduledRemoval();
1770
- this.clearPendingLoadedMeta();
1771
- this.clearMetadataProbe();
1772
- this._restoringPosition = false;
1773
- this.releaseElement({ resetState: true });
1774
- this.unsubscribeEventListeners?.();
1775
- this.unsubscribeEventListeners = null;
1776
- this.plugins.forEach(({ onRemove }) => onRemove?.({ player: this }));
1777
- this._pool.deregister(this.id);
1778
- };
1779
- this.cancelScheduledRemoval = () => {
1780
- clearTimeout(this._removalTimeout);
1781
- this._removalTimeout = void 0;
1782
- };
1783
- this.scheduleRemoval = (ms = 0) => {
1784
- this.cancelScheduledRemoval();
1785
- this._removalTimeout = setTimeout(() => {
1786
- if (this.disposed) return;
1787
- this.requestRemoval();
1788
- }, ms);
1789
- };
1790
- this.releaseElementForHandoff = () => {
1791
- if (!this.elementRef) return;
1792
- this.releaseElement({ resetState: false });
1793
- this.unsubscribeEventListeners?.();
1794
- this.unsubscribeEventListeners = null;
1795
- };
1796
- this.registerSubscriptions = () => {
1797
- this.unsubscribeEventListeners?.();
1798
- const audioElement = this.elementRef;
1799
- if (!audioElement) return;
1800
- const handleEnded = () => {
1801
- if (audioElement) {
1802
- this.updateDurationFromElement(audioElement);
1803
- }
1804
- this.stop();
1622
+ }
1623
+ case "copyStateFromChannelOnEvent": {
1624
+ const { channel } = action;
1625
+ return {
1626
+ ...state,
1627
+ members: { ...channel.state.members },
1628
+ messages: [...channel.state.messages],
1629
+ pinnedMessages: [...channel.state.pinnedMessages],
1630
+ read: { ...channel.state.read },
1631
+ watcherCount: channel.state.watcher_count,
1632
+ watchers: { ...channel.state.watchers }
1805
1633
  };
1806
- const handleError = (e) => {
1807
- const audio = e.currentTarget;
1808
- const state = { isPlaying: false };
1809
- if (!audio?.error?.code) {
1810
- this.state.partialNext(state);
1811
- return;
1812
- }
1813
- if (audio.error.code === 4) {
1814
- state.canPlayRecord = false;
1815
- this.state.partialNext(state);
1816
- }
1817
- const errorMsg = [
1818
- void 0,
1819
- "MEDIA_ERR_ABORTED: fetch aborted by user",
1820
- "MEDIA_ERR_NETWORK: network failed while fetching",
1821
- "MEDIA_ERR_DECODE: audio fetched but couldn’t decode",
1822
- "MEDIA_ERR_SRC_NOT_SUPPORTED: source not supported"
1823
- ][audio?.error?.code];
1824
- if (!errorMsg) return;
1825
- defaultRegisterAudioPlayerError({ error: new Error(errorMsg + ` (${audio.src})`) });
1634
+ }
1635
+ case "initStateFromChannel": {
1636
+ const { channel, hasMore } = action;
1637
+ return {
1638
+ ...state,
1639
+ hasMore,
1640
+ loading: false,
1641
+ members: { ...channel.state.members },
1642
+ messages: [...channel.state.messages],
1643
+ pinnedMessages: [...channel.state.pinnedMessages],
1644
+ read: { ...channel.state.read },
1645
+ watcherCount: channel.state.watcher_count,
1646
+ watchers: { ...channel.state.watchers }
1826
1647
  };
1827
- const handleTimeupdate = () => {
1828
- const t = audioElement?.currentTime ?? 0;
1829
- if (this._restoringPosition && t === 0) return;
1830
- if (!this.isPlaying && t === 0 && this.secondsElapsed > 0) return;
1831
- this.setSecondsElapsed(t);
1648
+ }
1649
+ case "jumpToLatestMessageFinished": {
1650
+ const { hasMore, hasMoreNewer, messages } = action;
1651
+ return {
1652
+ ...state,
1653
+ hasMore,
1654
+ hasMoreNewer,
1655
+ highlightedMessageId: void 0,
1656
+ loading: false,
1657
+ messages,
1658
+ suppressAutoscroll: false
1832
1659
  };
1833
- const handleLoadedMetadata = () => {
1834
- if (audioElement) {
1835
- this.updateDurationFromElement(audioElement);
1836
- }
1660
+ }
1661
+ case "jumpToMessageFinished": {
1662
+ return {
1663
+ ...state,
1664
+ hasMore: action.channel.state.messagePagination.hasPrev,
1665
+ hasMoreNewer: action.channel.state.messagePagination.hasNext,
1666
+ highlightedMessageId: action.highlightedMessageId,
1667
+ loadingMore: false,
1668
+ loadingMoreForJumpToChannelMessage: false,
1669
+ messages: action.channel.state.messages,
1670
+ suppressAutoscroll: false
1837
1671
  };
1838
- audioElement.addEventListener("ended", handleEnded);
1839
- audioElement.addEventListener("error", handleError);
1840
- audioElement.addEventListener("loadedmetadata", handleLoadedMetadata);
1841
- audioElement.addEventListener("timeupdate", handleTimeupdate);
1842
- this.unsubscribeEventListeners = () => {
1843
- audioElement.pause();
1844
- audioElement.removeEventListener("ended", handleEnded);
1845
- audioElement.removeEventListener("error", handleError);
1846
- audioElement.removeEventListener("loadedmetadata", handleLoadedMetadata);
1847
- audioElement.removeEventListener("timeupdate", handleTimeupdate);
1672
+ }
1673
+ case "clearHighlightedMessage": {
1674
+ return {
1675
+ ...state,
1676
+ highlightedMessageId: void 0
1848
1677
  };
1849
- };
1850
- this._data = {
1851
- durationSeconds,
1852
- fileSize,
1853
- id,
1854
- mimeType,
1855
- src,
1856
- title,
1857
- waveformData
1858
- };
1859
- this._pool = pool;
1860
- this.setPlugins(() => plugins ?? []);
1861
- const playbackRates = customPlaybackRates?.length ? customPlaybackRates : DEFAULT_PLAYBACK_RATES;
1862
- const canPlayRecord = mimeType ? !!new Audio().canPlayType(mimeType) : true;
1863
- this.state = new StateStore({
1864
- canPlayRecord,
1865
- currentPlaybackRate: playbackRates[0],
1866
- durationSeconds,
1867
- elementRef: null,
1868
- isPlaying: false,
1869
- playbackError: null,
1870
- playbackRates,
1871
- progressPercent: 0,
1872
- secondsElapsed: 0
1873
- });
1874
- this.plugins.forEach((p) => p.onInit?.({ player: this }));
1875
- this.preloadMetadata();
1876
- }
1877
- get plugins() {
1878
- return Array.from(this._plugins.values());
1879
- }
1880
- get canPlayRecord() {
1881
- return this.state.getLatestValue().canPlayRecord;
1882
- }
1883
- get elementRef() {
1884
- return this.state.getLatestValue().elementRef;
1885
- }
1886
- get isPlaying() {
1887
- return this.state.getLatestValue().isPlaying;
1888
- }
1889
- get currentPlaybackRate() {
1890
- return this.state.getLatestValue().currentPlaybackRate;
1891
- }
1892
- get playbackRates() {
1893
- return this.state.getLatestValue().playbackRates;
1894
- }
1895
- get durationSeconds() {
1896
- return this.state.getLatestValue().durationSeconds;
1897
- }
1898
- get fileSize() {
1899
- return this._data.fileSize;
1900
- }
1901
- get id() {
1902
- return this._data.id;
1903
- }
1904
- get src() {
1905
- return this._data.src;
1906
- }
1907
- get mimeType() {
1908
- return this._data.mimeType;
1909
- }
1910
- get title() {
1911
- return this._data.title;
1912
- }
1913
- get waveformData() {
1914
- return this._data.waveformData;
1915
- }
1916
- get secondsElapsed() {
1917
- return this.state.getLatestValue().secondsElapsed;
1918
- }
1919
- get progressPercent() {
1920
- return this.state.getLatestValue().progressPercent;
1921
- }
1922
- get disposed() {
1923
- return this._disposed;
1924
- }
1925
- ensureElementRef() {
1926
- if (this._disposed) {
1927
- throw new Error("AudioPlayer is disposed");
1928
1678
  }
1929
- if (!this.elementRef) {
1930
- const el = this._pool.acquireElement({
1931
- ownerId: this.id,
1932
- src: this.src
1933
- });
1934
- this.setRef(el);
1679
+ case "loadMoreFinished": {
1680
+ const { hasMore, messages } = action;
1681
+ return {
1682
+ ...state,
1683
+ hasMore,
1684
+ loadingMore: false,
1685
+ messages,
1686
+ suppressAutoscroll: false
1687
+ };
1935
1688
  }
1936
- return this.elementRef;
1937
- }
1938
- setDescriptor(descriptor) {
1939
- const previousSrc = this.src;
1940
- this._data = { ...this._data, ...descriptor };
1941
- if (descriptor.src !== previousSrc && this.elementRef) {
1942
- this.elementRef.src = descriptor.src;
1689
+ case "loadMoreNewerFinished": {
1690
+ const { hasMoreNewer, messages } = action;
1691
+ return {
1692
+ ...state,
1693
+ hasMoreNewer,
1694
+ loadingMoreNewer: false,
1695
+ messages
1696
+ };
1943
1697
  }
1944
- if (descriptor.src && descriptor.src !== previousSrc) {
1945
- this.clearMetadataProbe();
1946
- if (descriptor.durationSeconds == null) {
1947
- this.setDurationSeconds(void 0);
1948
- this.preloadMetadata();
1949
- } else {
1950
- this.setDurationSeconds(descriptor.durationSeconds);
1951
- }
1952
- return;
1698
+ case "loadMoreThreadFinished": {
1699
+ const { threadHasMore, threadMessages } = action;
1700
+ return {
1701
+ ...state,
1702
+ threadHasMore,
1703
+ threadLoadingMore: false,
1704
+ threadMessages
1705
+ };
1953
1706
  }
1954
- if (descriptor.durationSeconds != null) {
1955
- this.setDurationSeconds(descriptor.durationSeconds);
1707
+ case "openThread": {
1708
+ const { channel, message } = action;
1709
+ return {
1710
+ ...state,
1711
+ thread: message,
1712
+ threadHasMore: true,
1713
+ threadMessages: message.id ? { ...channel.state.threads }[message.id] || [] : [],
1714
+ threadSuppressAutoscroll: false
1715
+ };
1956
1716
  }
1957
- }
1958
- releaseElement({ resetState }) {
1959
- this.clearPendingLoadedMeta();
1960
- this.clearMetadataProbe();
1961
- this._restoringPosition = false;
1962
- if (resetState) {
1963
- this.stop();
1964
- } else {
1965
- this.state.partialNext({ isPlaying: false });
1966
- if (this.elementRef) {
1967
- try {
1968
- this.elementRef.pause();
1969
- } catch {
1970
- }
1971
- }
1717
+ case "setError": {
1718
+ const { error } = action;
1719
+ return { ...state, error };
1972
1720
  }
1973
- if (this.elementRef) {
1974
- this._pool.releaseElement(this.id);
1975
- this.setRef(null);
1721
+ case "setLoadingMore": {
1722
+ const { loadingMore } = action;
1723
+ return { ...state, loadingMore, suppressAutoscroll: loadingMore };
1976
1724
  }
1725
+ case "setLoadingMoreForJumpToChannelMessage": {
1726
+ const { loadingMoreForJumpToChannelMessage } = action;
1727
+ return {
1728
+ ...state,
1729
+ loadingMoreForJumpToChannelMessage,
1730
+ suppressAutoscroll: loadingMoreForJumpToChannelMessage
1731
+ };
1732
+ }
1733
+ case "setLoadingMoreNewer": {
1734
+ const { loadingMoreNewer } = action;
1735
+ return { ...state, loadingMoreNewer };
1736
+ }
1737
+ case "setThread": {
1738
+ const { message } = action;
1739
+ return { ...state, thread: message };
1740
+ }
1741
+ case "setTyping": {
1742
+ const { channel } = action;
1743
+ return {
1744
+ ...state,
1745
+ typing: { ...channel.state.typing }
1746
+ };
1747
+ }
1748
+ case "startLoadingThread": {
1749
+ return {
1750
+ ...state,
1751
+ threadLoadingMore: true,
1752
+ threadSuppressAutoscroll: true
1753
+ };
1754
+ }
1755
+ case "updateThreadOnEvent": {
1756
+ const { channel, message } = action;
1757
+ if (!state.thread) return state;
1758
+ return {
1759
+ ...state,
1760
+ thread: message?.id === state.thread.id ? channel.state.formatMessage(message) : state.thread,
1761
+ threadMessages: state.thread?.id ? { ...channel.state.threads }[state.thread.id] || [] : []
1762
+ };
1763
+ }
1764
+ default:
1765
+ return state;
1977
1766
  }
1978
- setPlugins(setter) {
1979
- this._plugins = setter(this.plugins).reduce((acc, plugin) => {
1980
- if (plugin.id) {
1981
- acc.set(plugin.id, plugin);
1982
- }
1983
- return acc;
1984
- }, /* @__PURE__ */ new Map());
1985
- }
1986
- }
1987
- class AudioPlayerPool {
1988
- constructor(config) {
1989
- this.state = new StateStore({
1990
- activeAudioPlayer: null
1991
- });
1992
- this.pool = /* @__PURE__ */ new Map();
1993
- this.audios = /* @__PURE__ */ new Map();
1994
- this.sharedAudio = null;
1995
- this.sharedOwnerId = null;
1996
- this.getOrAdd = (params) => {
1997
- const { playbackRates, plugins, ...descriptor } = params;
1998
- let player = this.pool.get(params.id);
1999
- if (player) {
2000
- if (!player.disposed) {
2001
- player.setDescriptor(descriptor);
2002
- return player;
2003
- }
2004
- this.deregister(params.id);
2005
- }
2006
- player = new AudioPlayer({
2007
- playbackRates,
2008
- plugins,
2009
- ...descriptor,
2010
- pool: this
2011
- });
2012
- this.pool.set(params.id, player);
2013
- return player;
2014
- };
2015
- this.acquireElement = ({ ownerId, src }) => {
2016
- if (!this.allowConcurrentPlayback) {
2017
- if (!this.sharedAudio) {
2018
- this.sharedAudio = new Audio();
2019
- }
2020
- if (this.sharedOwnerId && this.sharedOwnerId !== ownerId) {
2021
- const previous = this.pool.get(this.sharedOwnerId);
2022
- previous?.pause();
2023
- previous?.releaseElementForHandoff();
2024
- }
2025
- this.sharedOwnerId = ownerId;
2026
- if (this.sharedAudio.src !== src) {
2027
- this.sharedAudio.src = src;
2028
- }
2029
- return this.sharedAudio;
2030
- }
2031
- let audio = this.audios.get(ownerId);
2032
- if (!audio) {
2033
- audio = new Audio();
2034
- this.audios.set(ownerId, audio);
2035
- }
2036
- if (audio.src !== src) {
2037
- audio.src = src;
2038
- }
2039
- return audio;
2040
- };
2041
- this.releaseElement = (ownerId) => {
2042
- if (!this.allowConcurrentPlayback) {
2043
- if (this.sharedOwnerId !== ownerId) return;
2044
- const el2 = this.sharedAudio;
2045
- if (el2) {
2046
- try {
2047
- el2.pause();
2048
- } catch {
2049
- }
2050
- el2.removeAttribute("src");
2051
- el2.load();
2052
- }
2053
- this.sharedOwnerId = null;
2054
- return;
2055
- }
2056
- const el = this.audios.get(ownerId);
2057
- if (!el) return;
2058
- try {
2059
- el.pause();
2060
- } catch {
2061
- }
2062
- el.removeAttribute("src");
2063
- el.load();
2064
- this.audios.delete(ownerId);
2065
- };
2066
- this.setActiveAudioPlayer = (activeAudioPlayer) => {
2067
- if (this.allowConcurrentPlayback) return;
2068
- this.state.partialNext({ activeAudioPlayer });
2069
- };
2070
- this.remove = (id) => {
2071
- const player = this.pool.get(id);
2072
- if (!player) return;
2073
- player.requestRemoval();
2074
- };
2075
- this.clear = () => {
2076
- this.players.forEach((player) => {
2077
- this.remove(player.id);
2078
- });
2079
- };
2080
- this.registerSubscriptions = () => {
2081
- this.players.forEach((p) => {
2082
- if (p.elementRef) {
2083
- p.registerSubscriptions();
2084
- }
2085
- });
2086
- };
2087
- this.allowConcurrentPlayback = !!config?.allowConcurrentPlayback;
2088
- }
2089
- get players() {
2090
- return Array.from(this.pool.values());
2091
- }
2092
- get activeAudioPlayer() {
2093
- return this.state.getLatestValue().activeAudioPlayer;
2094
- }
2095
- /** Removes the AudioPlayer instance from the pool of players */
2096
- deregister(id) {
2097
- if (this.pool.has(id)) {
2098
- this.pool.delete(id);
2099
- }
2100
- if (this.activeAudioPlayer?.id === id) {
2101
- this.setActiveAudioPlayer(null);
2102
- }
2103
- }
2104
- }
2105
- const SEEK_NOT_SUPPORTED_NOTIFICATION_DEBOUNCE_INTERVAL_MS = 1e3;
2106
- const audioPlayerNotificationsPluginFactory = ({
2107
- addNotification,
2108
- panel = "channel",
2109
- t
2110
- }) => {
2111
- const errors = {
2112
- "failed-to-start": new Error(t("Failed to play the recording")),
2113
- "not-playable": new Error(
2114
- t("Recording format is not supported and cannot be reproduced")
2115
- ),
2116
- "seek-not-supported": new Error(t("Cannot seek in the recording"))
2117
- };
2118
- let lastSeekNotSupportedNotificationAt;
2119
- return {
2120
- id: "AudioPlayerNotificationsPlugin",
2121
- onError: ({ errCode, error: e }) => {
2122
- if (errCode === "seek-not-supported") {
2123
- const now = Date.now();
2124
- if (typeof lastSeekNotSupportedNotificationAt === "number" && now - lastSeekNotSupportedNotificationAt < SEEK_NOT_SUPPORTED_NOTIFICATION_DEBOUNCE_INTERVAL_MS) {
2125
- return;
2126
- }
2127
- lastSeekNotSupportedNotificationAt = now;
2128
- }
2129
- const error = (errCode && errors[errCode]) ?? e ?? new Error(t("Error reproducing the recording"));
2130
- addNotification({
2131
- emitter: "AudioPlayer",
2132
- error,
2133
- message: error.message,
2134
- severity: "error",
2135
- targetPanels: [panel],
2136
- type: "browser:audio:playback:error"
2137
- });
2138
- }
2139
- };
2140
- };
2141
- const NOTIFICATION_TARGET_PANELS = [
2142
- "channel",
2143
- "thread",
2144
- "channel-list",
2145
- "thread-list"
2146
- ];
2147
- const isNotificationTargetPanel = (value) => typeof value === "string" && NOTIFICATION_TARGET_PANELS.includes(value);
2148
- const getNotificationTargetPanel = (notification) => {
2149
- const targetTag = notification.tags?.find((tag) => tag.startsWith("target:"));
2150
- if (targetTag) {
2151
- const candidate = targetTag.slice("target:".length);
2152
- if (isNotificationTargetPanel(candidate)) return candidate;
2153
- }
2154
- const panel = notification.origin.context?.panel;
2155
- return isNotificationTargetPanel(panel) ? panel : void 0;
2156
- };
2157
- const getNotificationTargetPanels = (notification) => {
2158
- const targetPanels = (notification.tags ?? []).filter((tag) => tag.startsWith("target:")).map((tag) => tag.slice("target:".length)).filter(
2159
- (value) => isNotificationTargetPanel(value)
2160
- );
2161
- if (targetPanels.length > 0) {
2162
- return Array.from(new Set(targetPanels));
2163
- }
2164
- const panel = notification.origin.context?.panel;
2165
- return isNotificationTargetPanel(panel) ? [panel] : [];
2166
1767
  };
2167
- const getNotificationTargetTag = (panel) => `target:${panel}`;
2168
- const addNotificationTargetTag = (panel, tags) => {
2169
- if (!panel) return tags ?? [];
2170
- return Array.from(/* @__PURE__ */ new Set([getNotificationTargetTag(panel), ...tags ?? []]));
1768
+ const initialState = {
1769
+ error: null,
1770
+ hasMore: true,
1771
+ hasMoreNewer: false,
1772
+ loading: true,
1773
+ loadingMore: false,
1774
+ loadingMoreForJumpToChannelMessage: false,
1775
+ members: {},
1776
+ messages: [],
1777
+ pinnedMessages: [],
1778
+ read: {},
1779
+ suppressAutoscroll: false,
1780
+ thread: null,
1781
+ threadHasMore: true,
1782
+ threadLoadingMore: false,
1783
+ threadMessages: [],
1784
+ threadSuppressAutoscroll: false,
1785
+ typing: {},
1786
+ watcherCount: 0,
1787
+ watchers: {}
2171
1788
  };
2172
- const isNotificationForPanel = (notification, panel, options) => {
2173
- const explicitTargetPanels = getNotificationTargetPanels(notification);
2174
- if (explicitTargetPanels.length > 0) {
2175
- return explicitTargetPanels.includes(panel);
2176
- }
2177
- const resolvedPanel = options?.fallbackPanel ?? "channel";
2178
- return resolvedPanel === panel;
2179
- };
2180
- const variantToClass = {
2181
- danger: "str-chat__button--destructive",
2182
- primary: "str-chat__button--primary",
2183
- secondary: "str-chat__button--secondary"
2184
- };
2185
- const appearanceToClass = {
2186
- ghost: "str-chat__button--ghost",
2187
- outline: "str-chat__button--outline",
2188
- solid: "str-chat__button--solid"
2189
- };
2190
- const sizeToClass = {
2191
- lg: "str-chat__button--size-lg",
2192
- md: "str-chat__button--size-md",
2193
- sm: "str-chat__button--size-sm",
2194
- xs: "str-chat__button--size-xs"
2195
- };
2196
- const Button = forwardRef(function Button2({ appearance, children, circular, className, inverseTheme, size: size2, variant, ...props }, ref) {
2197
- return /* @__PURE__ */ jsx(
2198
- "button",
2199
- {
2200
- ref,
2201
- type: "button",
2202
- ...props,
2203
- className: clsx(
2204
- "str-chat__button",
2205
- variant != null && variantToClass[variant],
2206
- appearance != null && appearanceToClass[appearance],
2207
- circular && "str-chat__button--circular",
2208
- inverseTheme && "str-chat__theme-inverse",
2209
- size2 != null && sizeToClass[size2],
2210
- className
2211
- ),
2212
- children: /* @__PURE__ */ jsx("div", { className: "str-chat__button__content", children })
2213
- }
2214
- );
2215
- });
2216
- const UnMemoizedEmptyStateIndicator = (props) => {
2217
- const { listType, messageText } = props;
2218
- const { t } = useTranslationContext("EmptyStateIndicator");
2219
- if (listType === "thread") return null;
2220
- if (listType === "channel") {
2221
- const text = t("No conversations yet");
2222
- return /* @__PURE__ */ jsxs("div", { className: "str-chat__channel-list-empty", children: [
2223
- /* @__PURE__ */ jsx(IconMessageBubbles, {}),
2224
- /* @__PURE__ */ jsx("p", { role: "listitem", children: text })
2225
- ] });
2226
- }
2227
- if (listType === "message") {
2228
- const text = t(messageText || "Send a message to start the conversation");
2229
- return /* @__PURE__ */ jsxs("div", { className: "str-chat__empty-channel", children: [
2230
- /* @__PURE__ */ jsx(IconMessageBubble, {}),
2231
- /* @__PURE__ */ jsx("p", { className: "str-chat__empty-channel-text", role: "listitem", children: text })
2232
- ] });
2233
- }
2234
- return /* @__PURE__ */ jsx("p", { children: t("No items exist") });
2235
- };
2236
- const EmptyStateIndicator = React.memo(
2237
- UnMemoizedEmptyStateIndicator
2238
- );
2239
- const makeChannelReducer = () => (state, action) => {
2240
- switch (action.type) {
2241
- case "closeThread": {
2242
- return {
2243
- ...state,
2244
- thread: null,
2245
- threadLoadingMore: false,
2246
- threadMessages: []
2247
- };
2248
- }
2249
- case "copyMessagesFromChannel": {
2250
- const { channel, parentId } = action;
2251
- return {
2252
- ...state,
2253
- messages: [...channel.state.messages],
2254
- pinnedMessages: [...channel.state.pinnedMessages],
2255
- // copying messages from channel happens with new message - this resets the suppressAutoscroll
2256
- suppressAutoscroll: false,
2257
- threadMessages: parentId ? { ...channel.state.threads }[parentId] || [] : state.threadMessages
2258
- };
2259
- }
2260
- case "copyStateFromChannelOnEvent": {
2261
- const { channel } = action;
2262
- return {
2263
- ...state,
2264
- members: { ...channel.state.members },
2265
- messages: [...channel.state.messages],
2266
- pinnedMessages: [...channel.state.pinnedMessages],
2267
- read: { ...channel.state.read },
2268
- watcherCount: channel.state.watcher_count,
2269
- watchers: { ...channel.state.watchers }
2270
- };
2271
- }
2272
- case "initStateFromChannel": {
2273
- const { channel, hasMore } = action;
2274
- return {
2275
- ...state,
2276
- hasMore,
2277
- loading: false,
2278
- members: { ...channel.state.members },
2279
- messages: [...channel.state.messages],
2280
- pinnedMessages: [...channel.state.pinnedMessages],
2281
- read: { ...channel.state.read },
2282
- watcherCount: channel.state.watcher_count,
2283
- watchers: { ...channel.state.watchers }
2284
- };
2285
- }
2286
- case "jumpToLatestMessageFinished": {
2287
- const { hasMore, hasMoreNewer, messages } = action;
2288
- return {
2289
- ...state,
2290
- hasMore,
2291
- hasMoreNewer,
2292
- highlightedMessageId: void 0,
2293
- loading: false,
2294
- messages,
2295
- suppressAutoscroll: false
2296
- };
2297
- }
2298
- case "jumpToMessageFinished": {
2299
- return {
2300
- ...state,
2301
- hasMore: action.channel.state.messagePagination.hasPrev,
2302
- hasMoreNewer: action.channel.state.messagePagination.hasNext,
2303
- highlightedMessageId: action.highlightedMessageId,
2304
- loadingMore: false,
2305
- loadingMoreForJumpToChannelMessage: false,
2306
- messages: action.channel.state.messages,
2307
- suppressAutoscroll: false
2308
- };
2309
- }
2310
- case "clearHighlightedMessage": {
2311
- return {
2312
- ...state,
2313
- highlightedMessageId: void 0
2314
- };
2315
- }
2316
- case "loadMoreFinished": {
2317
- const { hasMore, messages } = action;
2318
- return {
2319
- ...state,
2320
- hasMore,
2321
- loadingMore: false,
2322
- messages,
2323
- suppressAutoscroll: false
2324
- };
2325
- }
2326
- case "loadMoreNewerFinished": {
2327
- const { hasMoreNewer, messages } = action;
2328
- return {
2329
- ...state,
2330
- hasMoreNewer,
2331
- loadingMoreNewer: false,
2332
- messages
2333
- };
2334
- }
2335
- case "loadMoreThreadFinished": {
2336
- const { threadHasMore, threadMessages } = action;
2337
- return {
2338
- ...state,
2339
- threadHasMore,
2340
- threadLoadingMore: false,
2341
- threadMessages
2342
- };
2343
- }
2344
- case "openThread": {
2345
- const { channel, message } = action;
2346
- return {
2347
- ...state,
2348
- thread: message,
2349
- threadHasMore: true,
2350
- threadMessages: message.id ? { ...channel.state.threads }[message.id] || [] : [],
2351
- threadSuppressAutoscroll: false
2352
- };
2353
- }
2354
- case "setError": {
2355
- const { error } = action;
2356
- return { ...state, error };
2357
- }
2358
- case "setLoadingMore": {
2359
- const { loadingMore } = action;
2360
- return { ...state, loadingMore, suppressAutoscroll: loadingMore };
2361
- }
2362
- case "setLoadingMoreForJumpToChannelMessage": {
2363
- const { loadingMoreForJumpToChannelMessage } = action;
2364
- return {
2365
- ...state,
2366
- loadingMoreForJumpToChannelMessage,
2367
- suppressAutoscroll: loadingMoreForJumpToChannelMessage
2368
- };
2369
- }
2370
- case "setLoadingMoreNewer": {
2371
- const { loadingMoreNewer } = action;
2372
- return { ...state, loadingMoreNewer };
2373
- }
2374
- case "setThread": {
2375
- const { message } = action;
2376
- return { ...state, thread: message };
2377
- }
2378
- case "setTyping": {
2379
- const { channel } = action;
2380
- return {
2381
- ...state,
2382
- typing: { ...channel.state.typing }
2383
- };
2384
- }
2385
- case "startLoadingThread": {
2386
- return {
2387
- ...state,
2388
- threadLoadingMore: true,
2389
- threadSuppressAutoscroll: true
2390
- };
2391
- }
2392
- case "updateThreadOnEvent": {
2393
- const { channel, message } = action;
2394
- if (!state.thread) return state;
2395
- return {
2396
- ...state,
2397
- thread: message?.id === state.thread.id ? channel.state.formatMessage(message) : state.thread,
2398
- threadMessages: state.thread?.id ? { ...channel.state.threads }[state.thread.id] || [] : []
2399
- };
2400
- }
2401
- default:
2402
- return state;
2403
- }
2404
- };
2405
- const initialState = {
2406
- error: null,
2407
- hasMore: true,
2408
- hasMoreNewer: false,
2409
- loading: true,
2410
- loadingMore: false,
2411
- loadingMoreForJumpToChannelMessage: false,
2412
- members: {},
2413
- messages: [],
2414
- pinnedMessages: [],
2415
- read: {},
2416
- suppressAutoscroll: false,
2417
- thread: null,
2418
- threadHasMore: true,
2419
- threadLoadingMore: false,
2420
- threadMessages: [],
2421
- threadSuppressAutoscroll: false,
2422
- typing: {},
2423
- watcherCount: 0,
2424
- watchers: {}
2425
- };
2426
- const useCreateChannelStateContext = (value) => {
2427
- const {
2428
- channel,
2429
- channelCapabilitiesArray = [],
2430
- channelConfig,
2431
- channelUnreadUiState,
2432
- error,
2433
- giphyVersion,
2434
- hasMore,
2435
- hasMoreNewer,
2436
- highlightedMessageId,
2437
- imageAttachmentSizeHandler,
2438
- loading,
2439
- loadingMore,
2440
- loadingMoreForJumpToChannelMessage,
2441
- members,
2442
- messages = [],
2443
- mutes,
2444
- notifications,
2445
- pinnedMessages,
2446
- read = {},
2447
- shouldGenerateVideoThumbnail,
2448
- skipMessageDataMemoization,
2449
- suppressAutoscroll,
2450
- thread,
2451
- threadHasMore,
2452
- threadLoadingMore,
2453
- threadMessages = [],
2454
- videoAttachmentSizeHandler,
2455
- watcher_count,
2456
- watcherCount,
2457
- watchers
2458
- } = value;
2459
- const channelId = channel.cid;
2460
- const lastRead = channel.initialized && channel.lastRead()?.getTime();
2461
- const membersLength = Object.keys(members || []).length;
2462
- const notificationsLength = notifications.length;
2463
- const readUsers = Object.values(read);
2464
- const readUsersLength = readUsers.length;
2465
- const readUsersLastReadDateStrings = [];
2466
- for (const { last_read } of readUsers) {
2467
- if (!lastRead) continue;
2468
- readUsersLastReadDateStrings.push(last_read?.toISOString());
1789
+ const useCreateChannelStateContext = (value) => {
1790
+ const {
1791
+ channel,
1792
+ channelCapabilitiesArray = [],
1793
+ channelConfig,
1794
+ channelUnreadUiState,
1795
+ error,
1796
+ giphyVersion,
1797
+ hasMore,
1798
+ hasMoreNewer,
1799
+ highlightedMessageId,
1800
+ imageAttachmentSizeHandler,
1801
+ loading,
1802
+ loadingMore,
1803
+ loadingMoreForJumpToChannelMessage,
1804
+ members,
1805
+ messages = [],
1806
+ mutes,
1807
+ notifications,
1808
+ pinnedMessages,
1809
+ read = {},
1810
+ shouldGenerateVideoThumbnail,
1811
+ skipMessageDataMemoization,
1812
+ suppressAutoscroll,
1813
+ thread,
1814
+ threadHasMore,
1815
+ threadLoadingMore,
1816
+ threadMessages = [],
1817
+ videoAttachmentSizeHandler,
1818
+ watcher_count,
1819
+ watcherCount,
1820
+ watchers
1821
+ } = value;
1822
+ const channelId = channel.cid;
1823
+ const lastRead = channel.initialized && channel.lastRead()?.getTime();
1824
+ const membersLength = Object.keys(members || []).length;
1825
+ const notificationsLength = notifications.length;
1826
+ const readUsers = Object.values(read);
1827
+ const readUsersLength = readUsers.length;
1828
+ const readUsersLastReadDateStrings = [];
1829
+ for (const { last_read } of readUsers) {
1830
+ if (!lastRead) continue;
1831
+ readUsersLastReadDateStrings.push(last_read?.toISOString());
2469
1832
  }
2470
1833
  const readUsersLastReads = readUsersLastReadDateStrings.join();
2471
1834
  const threadMessagesLength = threadMessages?.length;
@@ -2582,359 +1945,1067 @@ const useIsMounted = () => {
2582
1945
  return () => {
2583
1946
  isMounted.current = false;
2584
1947
  };
2585
- }, []);
2586
- return isMounted;
2587
- };
2588
- const useMentionsHandlers = (onMentionsHover, onMentionsClick) => useCallback(
2589
- (event, mentioned_users) => {
2590
- if (!onMentionsHover && !onMentionsClick || !(event.target instanceof HTMLElement)) {
2591
- return;
2592
- }
2593
- const target = event.target;
2594
- const textContent = target.innerHTML.replace("*", "");
2595
- if (textContent[0] === "@") {
2596
- const userName = textContent.replace("@", "");
2597
- const user = mentioned_users?.find(
2598
- ({ id, name }) => name === userName || id === userName
2599
- );
2600
- if (onMentionsHover && typeof onMentionsHover === "function" && event.type === "mouseover") {
2601
- onMentionsHover(event, user);
2602
- }
2603
- if (onMentionsClick && event.type === "click" && typeof onMentionsClick === "function") {
2604
- onMentionsClick(event, user);
2605
- }
2606
- }
2607
- },
2608
- [onMentionsClick, onMentionsHover]
2609
- );
2610
- const LoadingMessage = ({
2611
- bubbleSize,
2612
- metadataSize,
2613
- outgoing = false
2614
- }) => /* @__PURE__ */ jsxs(
2615
- "div",
2616
- {
2617
- className: `str-chat__loading-channel-message ${outgoing ? "str-chat__loading-channel-message--outgoing" : "str-chat__loading-channel-message--incoming"}`,
2618
- children: [
2619
- !outgoing ? /* @__PURE__ */ jsx("div", { className: "str-chat__loading-channel-message-avatar" }) : null,
2620
- /* @__PURE__ */ jsxs("div", { className: "str-chat__loading-channel-message-content", children: [
2621
- /* @__PURE__ */ jsx(
2622
- "div",
2623
- {
2624
- className: `str-chat__loading-channel-message-bubble str-chat__loading-channel-message-bubble--${bubbleSize}`
2625
- }
2626
- ),
2627
- /* @__PURE__ */ jsx(
2628
- "div",
2629
- {
2630
- className: `str-chat__loading-channel-message-metadata str-chat__loading-channel-message-metadata--${metadataSize}`
2631
- }
2632
- )
2633
- ] })
2634
- ]
2635
- }
2636
- );
2637
- const LoadingMessageInput = () => /* @__PURE__ */ jsx("div", { className: "str-chat__message-composer-container str-chat__message-composer-container--loading", children: /* @__PURE__ */ jsxs("div", { className: "str-chat__message-composer", children: [
2638
- /* @__PURE__ */ jsx("div", { className: "str-chat__loading-channel-message-input-button" }),
2639
- /* @__PURE__ */ jsx("div", { className: "str-chat__loading-channel-message-input-pill" })
2640
- ] }) });
2641
- const LoadingChannelHeader = () => /* @__PURE__ */ jsxs("div", { className: "str-chat__channel-header str-chat__channel-header--loading", children: [
2642
- /* @__PURE__ */ jsx("div", { className: "str-chat__channel-header__data str-chat__channel-header__data--loading", children: /* @__PURE__ */ jsx("div", { className: "str-chat__loading-channel-header-name" }) }),
2643
- /* @__PURE__ */ jsx("div", { className: "str-chat__loading-channel-header-avatar" })
2644
- ] });
2645
- const LoadingChannel = () => /* @__PURE__ */ jsxs("div", { className: "str-chat__loading-channel", children: [
2646
- /* @__PURE__ */ jsx(LoadingChannelHeader, {}),
2647
- /* @__PURE__ */ jsx("div", { className: "str-chat__message-list str-chat__message-list--loading", children: /* @__PURE__ */ jsx("div", { className: "str-chat__message-list-scroll", children: /* @__PURE__ */ jsxs("div", { className: "str-chat__loading-channel-message-list", children: [
2648
- /* @__PURE__ */ jsx(LoadingMessage, { bubbleSize: "lg", metadataSize: "md" }),
2649
- /* @__PURE__ */ jsx(LoadingMessage, { bubbleSize: "md", metadataSize: "sm", outgoing: true }),
2650
- /* @__PURE__ */ jsx(LoadingMessage, { bubbleSize: "lg", metadataSize: "md" })
2651
- ] }) }) }),
2652
- /* @__PURE__ */ jsx(LoadingMessageInput, {})
2653
- ] });
2654
- const UnMemoizedLoadingErrorIndicator = ({ error }) => {
2655
- const { t } = useTranslationContext("LoadingErrorIndicator");
2656
- if (!error) return null;
2657
- return /* @__PURE__ */ jsx("div", { children: t("Error: {{ errorMessage }}", { errorMessage: error.message }) });
2658
- };
2659
- const LoadingErrorIndicator = React.memo(
2660
- UnMemoizedLoadingErrorIndicator,
2661
- (prevProps, nextProps) => prevProps.error?.message === nextProps.error?.message
2662
- );
2663
- const CHANNEL_CONTAINER_ID = "str-chat__channel";
2664
- const DEFAULT_NEXT_CHANNEL_PAGE_SIZE = 25;
2665
- const DEFAULT_JUMP_TO_PAGE_SIZE = 25;
2666
- const DEFAULT_THREAD_PAGE_SIZE = 25;
2667
- const DEFAULT_LOAD_PAGE_SCROLL_THRESHOLD = 250;
2668
- const DEFAULT_HIGHLIGHT_DURATION = 500;
2669
- const validateAndGetMessage = (func, args) => {
2670
- if (!func || typeof func !== "function") return null;
2671
- if (!Array.isArray(args)) {
2672
- args = [args];
2673
- }
2674
- const returnValue = func(...args);
2675
- if (typeof returnValue !== "string") return null;
2676
- return returnValue;
2677
- };
2678
- const isUserMuted = (message, mutes) => {
2679
- if (!mutes || !message) return false;
2680
- const userMuted = mutes.filter((el) => el.target.id === message.user?.id);
2681
- return !!userMuted.length;
2682
- };
2683
- const OPTIONAL_MESSAGE_ACTIONS = {
2684
- deleteForMe: "deleteForMe"
2685
- };
2686
- const MESSAGE_ACTIONS = {
2687
- delete: "delete",
2688
- download: "download",
2689
- edit: "edit",
2690
- flag: "flag",
2691
- markUnread: "markUnread",
2692
- mute: "mute",
2693
- pin: "pin",
2694
- quote: "quote",
2695
- react: "react",
2696
- remindMe: "remindMe",
2697
- reply: "reply",
2698
- saveForLater: "saveForLater"
2699
- };
2700
- const getMessageActions = (actions, {
2701
- canDelete,
2702
- canEdit,
2703
- canFlag,
2704
- canMarkUnread,
2705
- canMute,
2706
- canPin,
2707
- canQuote,
2708
- canReact,
2709
- canReply
2710
- }, channelConfig) => {
2711
- const messageActionsAfterPermission = [];
2712
- let messageActions = [];
2713
- if (actions && typeof actions === "boolean") {
2714
- messageActions = Object.keys(MESSAGE_ACTIONS);
2715
- } else if (actions && Array.isArray(actions) && actions.length > 0) {
2716
- messageActions = [...actions];
2717
- } else {
2718
- return [];
2719
- }
2720
- if (canDelete && messageActions.indexOf(MESSAGE_ACTIONS.delete) > -1) {
2721
- messageActionsAfterPermission.push(MESSAGE_ACTIONS.delete);
1948
+ }, []);
1949
+ return isMounted;
1950
+ };
1951
+ const useMentionsHandlers = (onMentionsHover, onMentionsClick) => useCallback(
1952
+ (event, mentioned_users) => {
1953
+ if (!onMentionsHover && !onMentionsClick || !(event.target instanceof HTMLElement)) {
1954
+ return;
1955
+ }
1956
+ const target = event.target;
1957
+ const textContent = target.innerHTML.replace("*", "");
1958
+ if (textContent[0] === "@") {
1959
+ const userName = textContent.replace("@", "");
1960
+ const user = mentioned_users?.find(
1961
+ ({ id, name }) => name === userName || id === userName
1962
+ );
1963
+ if (onMentionsHover && typeof onMentionsHover === "function" && event.type === "mouseover") {
1964
+ onMentionsHover(event, user);
1965
+ }
1966
+ if (onMentionsClick && event.type === "click" && typeof onMentionsClick === "function") {
1967
+ onMentionsClick(event, user);
1968
+ }
1969
+ }
1970
+ },
1971
+ [onMentionsClick, onMentionsHover]
1972
+ );
1973
+ const LoadingMessage = ({
1974
+ bubbleSize,
1975
+ metadataSize,
1976
+ outgoing = false
1977
+ }) => /* @__PURE__ */ jsxs(
1978
+ "div",
1979
+ {
1980
+ className: `str-chat__loading-channel-message ${outgoing ? "str-chat__loading-channel-message--outgoing" : "str-chat__loading-channel-message--incoming"}`,
1981
+ children: [
1982
+ !outgoing ? /* @__PURE__ */ jsx("div", { className: "str-chat__loading-channel-message-avatar" }) : null,
1983
+ /* @__PURE__ */ jsxs("div", { className: "str-chat__loading-channel-message-content", children: [
1984
+ /* @__PURE__ */ jsx(
1985
+ "div",
1986
+ {
1987
+ className: `str-chat__loading-channel-message-bubble str-chat__loading-channel-message-bubble--${bubbleSize}`
1988
+ }
1989
+ ),
1990
+ /* @__PURE__ */ jsx(
1991
+ "div",
1992
+ {
1993
+ className: `str-chat__loading-channel-message-metadata str-chat__loading-channel-message-metadata--${metadataSize}`
1994
+ }
1995
+ )
1996
+ ] })
1997
+ ]
1998
+ }
1999
+ );
2000
+ const LoadingMessageInput = () => /* @__PURE__ */ jsx("div", { className: "str-chat__message-composer-container str-chat__message-composer-container--loading", children: /* @__PURE__ */ jsxs("div", { className: "str-chat__message-composer", children: [
2001
+ /* @__PURE__ */ jsx("div", { className: "str-chat__loading-channel-message-input-button" }),
2002
+ /* @__PURE__ */ jsx("div", { className: "str-chat__loading-channel-message-input-pill" })
2003
+ ] }) });
2004
+ const LoadingChannelHeader = () => /* @__PURE__ */ jsxs("div", { className: "str-chat__channel-header str-chat__channel-header--loading", children: [
2005
+ /* @__PURE__ */ jsx("div", { className: "str-chat__channel-header__data str-chat__channel-header__data--loading", children: /* @__PURE__ */ jsx("div", { className: "str-chat__loading-channel-header-name" }) }),
2006
+ /* @__PURE__ */ jsx("div", { className: "str-chat__loading-channel-header-avatar" })
2007
+ ] });
2008
+ const LoadingChannel = () => /* @__PURE__ */ jsxs("div", { className: "str-chat__loading-channel", children: [
2009
+ /* @__PURE__ */ jsx(LoadingChannelHeader, {}),
2010
+ /* @__PURE__ */ jsx("div", { className: "str-chat__message-list str-chat__message-list--loading", children: /* @__PURE__ */ jsx("div", { className: "str-chat__message-list-scroll", children: /* @__PURE__ */ jsxs("div", { className: "str-chat__loading-channel-message-list", children: [
2011
+ /* @__PURE__ */ jsx(LoadingMessage, { bubbleSize: "lg", metadataSize: "md" }),
2012
+ /* @__PURE__ */ jsx(LoadingMessage, { bubbleSize: "md", metadataSize: "sm", outgoing: true }),
2013
+ /* @__PURE__ */ jsx(LoadingMessage, { bubbleSize: "lg", metadataSize: "md" })
2014
+ ] }) }) }),
2015
+ /* @__PURE__ */ jsx(LoadingMessageInput, {})
2016
+ ] });
2017
+ const UnMemoizedLoadingErrorIndicator = ({ error }) => {
2018
+ const { t } = useTranslationContext("LoadingErrorIndicator");
2019
+ if (!error) return null;
2020
+ return /* @__PURE__ */ jsx("div", { children: t("Error: {{ errorMessage }}", { errorMessage: error.message }) });
2021
+ };
2022
+ const LoadingErrorIndicator = React.memo(
2023
+ UnMemoizedLoadingErrorIndicator,
2024
+ (prevProps, nextProps) => prevProps.error?.message === nextProps.error?.message
2025
+ );
2026
+ const CHANNEL_CONTAINER_ID = "str-chat__channel";
2027
+ const DEFAULT_NEXT_CHANNEL_PAGE_SIZE = 25;
2028
+ const DEFAULT_JUMP_TO_PAGE_SIZE = 25;
2029
+ const DEFAULT_THREAD_PAGE_SIZE = 25;
2030
+ const DEFAULT_LOAD_PAGE_SCROLL_THRESHOLD = 250;
2031
+ const DEFAULT_HIGHLIGHT_DURATION = 500;
2032
+ const validateAndGetMessage = (func, args) => {
2033
+ if (!func || typeof func !== "function") return null;
2034
+ if (!Array.isArray(args)) {
2035
+ args = [args];
2036
+ }
2037
+ const returnValue = func(...args);
2038
+ if (typeof returnValue !== "string") return null;
2039
+ return returnValue;
2040
+ };
2041
+ const isUserMuted = (message, mutes) => {
2042
+ if (!mutes || !message) return false;
2043
+ const userMuted = mutes.filter((el) => el.target.id === message.user?.id);
2044
+ return !!userMuted.length;
2045
+ };
2046
+ const OPTIONAL_MESSAGE_ACTIONS = {
2047
+ deleteForMe: "deleteForMe"
2048
+ };
2049
+ const MESSAGE_ACTIONS = {
2050
+ delete: "delete",
2051
+ download: "download",
2052
+ edit: "edit",
2053
+ flag: "flag",
2054
+ markUnread: "markUnread",
2055
+ mute: "mute",
2056
+ pin: "pin",
2057
+ quote: "quote",
2058
+ react: "react",
2059
+ remindMe: "remindMe",
2060
+ reply: "reply",
2061
+ saveForLater: "saveForLater"
2062
+ };
2063
+ const getMessageActions = (actions, {
2064
+ canDelete,
2065
+ canEdit,
2066
+ canFlag,
2067
+ canMarkUnread,
2068
+ canMute,
2069
+ canPin,
2070
+ canQuote,
2071
+ canReact,
2072
+ canReply
2073
+ }, channelConfig) => {
2074
+ const messageActionsAfterPermission = [];
2075
+ let messageActions = [];
2076
+ if (actions && typeof actions === "boolean") {
2077
+ messageActions = Object.keys(MESSAGE_ACTIONS);
2078
+ } else if (actions && Array.isArray(actions) && actions.length > 0) {
2079
+ messageActions = [...actions];
2080
+ } else {
2081
+ return [];
2082
+ }
2083
+ if (canDelete && messageActions.indexOf(MESSAGE_ACTIONS.delete) > -1) {
2084
+ messageActionsAfterPermission.push(MESSAGE_ACTIONS.delete);
2085
+ }
2086
+ if (messageActions.indexOf(MESSAGE_ACTIONS.download) > -1) {
2087
+ messageActionsAfterPermission.push(MESSAGE_ACTIONS.download);
2088
+ }
2089
+ if (canDelete && messageActions.indexOf(OPTIONAL_MESSAGE_ACTIONS.deleteForMe) > -1) {
2090
+ messageActionsAfterPermission.push(OPTIONAL_MESSAGE_ACTIONS.deleteForMe);
2091
+ }
2092
+ if (canEdit && messageActions.indexOf(MESSAGE_ACTIONS.edit) > -1) {
2093
+ messageActionsAfterPermission.push(MESSAGE_ACTIONS.edit);
2094
+ }
2095
+ if (canFlag && messageActions.indexOf(MESSAGE_ACTIONS.flag) > -1) {
2096
+ messageActionsAfterPermission.push(MESSAGE_ACTIONS.flag);
2097
+ }
2098
+ if (canMarkUnread && messageActions.indexOf(MESSAGE_ACTIONS.markUnread) > -1) {
2099
+ messageActionsAfterPermission.push(MESSAGE_ACTIONS.markUnread);
2100
+ }
2101
+ if (canMute && messageActions.indexOf(MESSAGE_ACTIONS.mute) > -1) {
2102
+ messageActionsAfterPermission.push(MESSAGE_ACTIONS.mute);
2103
+ }
2104
+ if (canPin && messageActions.indexOf(MESSAGE_ACTIONS.pin) > -1) {
2105
+ messageActionsAfterPermission.push(MESSAGE_ACTIONS.pin);
2106
+ }
2107
+ if (canQuote && messageActions.indexOf(MESSAGE_ACTIONS.quote) > -1) {
2108
+ messageActionsAfterPermission.push(MESSAGE_ACTIONS.quote);
2109
+ }
2110
+ if (canReact && messageActions.indexOf(MESSAGE_ACTIONS.react) > -1) {
2111
+ messageActionsAfterPermission.push(MESSAGE_ACTIONS.react);
2112
+ }
2113
+ if (channelConfig?.["user_message_reminders"] && messageActions.indexOf(MESSAGE_ACTIONS.remindMe) > -1) {
2114
+ messageActionsAfterPermission.push(MESSAGE_ACTIONS.remindMe);
2115
+ }
2116
+ if (canReply && messageActions.indexOf(MESSAGE_ACTIONS.reply) > -1) {
2117
+ messageActionsAfterPermission.push(MESSAGE_ACTIONS.reply);
2118
+ }
2119
+ if (channelConfig?.["user_message_reminders"] && messageActions.indexOf(MESSAGE_ACTIONS.saveForLater) > -1) {
2120
+ messageActionsAfterPermission.push(MESSAGE_ACTIONS.saveForLater);
2121
+ }
2122
+ return messageActionsAfterPermission;
2123
+ };
2124
+ const ACTIONS_NOT_WORKING_IN_THREAD = [
2125
+ MESSAGE_ACTIONS.pin,
2126
+ MESSAGE_ACTIONS.reply,
2127
+ MESSAGE_ACTIONS.markUnread
2128
+ ];
2129
+ function areMessagesEqual(prevMessage, nextMessage) {
2130
+ const areBaseMessagesEqual = (prevMessage2, nextMessage2) => prevMessage2.deleted_at === nextMessage2.deleted_at && prevMessage2.latest_reactions?.length === nextMessage2.latest_reactions?.length && prevMessage2.own_reactions?.length === nextMessage2.own_reactions?.length && prevMessage2.pinned === nextMessage2.pinned && prevMessage2.reply_count === nextMessage2.reply_count && prevMessage2.show_in_channel === nextMessage2.show_in_channel && prevMessage2.status === nextMessage2.status && prevMessage2.text === nextMessage2.text && prevMessage2.type === nextMessage2.type && prevMessage2.updated_at === nextMessage2.updated_at && prevMessage2.user?.updated_at === nextMessage2.user?.updated_at;
2131
+ return areBaseMessagesEqual(prevMessage, nextMessage) && Boolean(prevMessage.quoted_message) === Boolean(nextMessage.quoted_message) && (!prevMessage.quoted_message && !nextMessage.quoted_message || areBaseMessagesEqual(
2132
+ prevMessage.quoted_message,
2133
+ nextMessage.quoted_message
2134
+ ));
2135
+ }
2136
+ const areMessagePropsEqual = (prevProps, nextProps) => {
2137
+ const { message: prevMessage, Message: prevMessageUI } = prevProps;
2138
+ const { message: nextMessage, Message: nextMessageUI } = nextProps;
2139
+ if (prevMessageUI !== nextMessageUI) return false;
2140
+ if (nextProps.showDetailedReactions !== prevProps.showDetailedReactions) {
2141
+ return false;
2142
+ }
2143
+ if (nextProps.closeReactionSelectorOnClick !== prevProps.closeReactionSelectorOnClick) {
2144
+ return false;
2145
+ }
2146
+ const messagesAreEqual = areMessagesEqual(prevMessage, nextMessage);
2147
+ if (!messagesAreEqual) return false;
2148
+ const deepEqualProps = deepequal(nextProps.messageActions, prevProps.messageActions) && deepequal(nextProps.readBy, prevProps.readBy) && deepequal(nextProps.deliveredTo, prevProps.deliveredTo) && deepequal(nextProps.highlighted, prevProps.highlighted) && deepequal(nextProps.groupStyles, prevProps.groupStyles) && // last 3 messages can have different group styles
2149
+ deepequal(nextProps.mutes, prevProps.mutes) && deepequal(nextProps.lastReceivedId, prevProps.lastReceivedId);
2150
+ if (!deepEqualProps) return false;
2151
+ return prevProps.messageListRect === nextProps.messageListRect;
2152
+ };
2153
+ const areMessageUIPropsEqual = (prevProps, nextProps) => {
2154
+ const { lastReceivedId: prevLastReceivedId, message: prevMessage } = prevProps;
2155
+ const { lastReceivedId: nextLastReceivedId, message: nextMessage } = nextProps;
2156
+ if (prevProps.highlighted !== nextProps.highlighted) return false;
2157
+ if (prevProps.threadList !== nextProps.threadList) return false;
2158
+ if (prevProps.endOfGroup !== nextProps.endOfGroup) return false;
2159
+ if (prevProps.mutes?.length !== nextProps.mutes?.length) return false;
2160
+ if (prevProps.readBy?.length !== nextProps.readBy?.length) return false;
2161
+ if (prevProps.deliveredTo?.length !== nextProps.deliveredTo?.length) return false;
2162
+ if (prevProps.groupStyles !== nextProps.groupStyles) return false;
2163
+ if (prevProps.showDetailedReactions !== nextProps.showDetailedReactions) {
2164
+ return false;
2165
+ }
2166
+ if ((prevMessage.id === prevLastReceivedId || prevMessage.id === nextLastReceivedId) && prevLastReceivedId !== nextLastReceivedId) {
2167
+ return false;
2168
+ }
2169
+ return areMessagesEqual(prevMessage, nextMessage);
2170
+ };
2171
+ const messageHasReactions = (message) => Object.values(message?.reaction_groups ?? {}).some(({ count }) => count > 0);
2172
+ const messageHasQuotedMessage = (message) => !!message?.quoted_message;
2173
+ const messageHasAttachments = (message) => !!message?.attachments && !!message.attachments.length;
2174
+ const messageHasSingleAttachment = (message) => message?.attachments?.length === 1;
2175
+ const messageHasGiphyAttachment = (message) => !!message?.attachments?.some((att) => att.type === "giphy");
2176
+ const getImages = (message) => {
2177
+ if (!message?.attachments) {
2178
+ return [];
2179
+ }
2180
+ return message.attachments.filter((item) => item.type === "image");
2181
+ };
2182
+ const getNonImageAttachments = (message) => {
2183
+ if (!message?.attachments) {
2184
+ return [];
2185
+ }
2186
+ return message.attachments.filter((item) => item.type !== "image");
2187
+ };
2188
+ const mapToUserNameOrId = (user) => user.name || user.id;
2189
+ const getReadByTooltipText = (users, t, client, tooltipUserNameMapper) => {
2190
+ let outStr = "";
2191
+ if (!t) {
2192
+ throw new Error(
2193
+ "getReadByTooltipText was called, but translation function is not available"
2194
+ );
2195
+ }
2196
+ if (!tooltipUserNameMapper) {
2197
+ throw new Error(
2198
+ "getReadByTooltipText was called, but tooltipUserNameMapper function is not available"
2199
+ );
2200
+ }
2201
+ const otherUsers = users.filter((item) => item && client?.user && item.id !== client.user.id).map(tooltipUserNameMapper);
2202
+ const slicedArr = otherUsers.slice(0, 5);
2203
+ const restLength = otherUsers.length - slicedArr.length;
2204
+ if (slicedArr.length === 1) {
2205
+ outStr = `${slicedArr[0]} `;
2206
+ } else if (slicedArr.length === 2) {
2207
+ outStr = t("{{ firstUser }} and {{ secondUser }}", {
2208
+ firstUser: slicedArr[0],
2209
+ secondUser: slicedArr[1]
2210
+ });
2211
+ } else if (slicedArr.length > 2) {
2212
+ if (restLength === 0) {
2213
+ const lastUser = slicedArr.splice(slicedArr.length - 1, 1);
2214
+ outStr = t("{{ commaSeparatedUsers }}, and {{ lastUser }}", {
2215
+ commaSeparatedUsers: slicedArr.join(", "),
2216
+ lastUser
2217
+ });
2218
+ } else {
2219
+ outStr = t("{{ commaSeparatedUsers }} and {{ moreCount }} more", {
2220
+ commaSeparatedUsers: slicedArr.join(", "),
2221
+ moreCount: restLength
2222
+ });
2223
+ }
2224
+ }
2225
+ return outStr;
2226
+ };
2227
+ const countEmojis = (text) => {
2228
+ const matches = text?.match(emojiRegex());
2229
+ return matches ? matches.length : 0;
2230
+ };
2231
+ const messageTextHasEmojisOnly = (message) => {
2232
+ if (!message.text) return false;
2233
+ const noEmojis = message.text.replace(emojiRegex(), "");
2234
+ const noSpace = noEmojis.replace(/[\s\n]/gm, "");
2235
+ return !noSpace;
2236
+ };
2237
+ const isMessageErrorRetryable = (message) => message.status === "failed" && message.error?.status !== 403;
2238
+ const isNetworkSendFailure = (message) => message.status === "failed" && message.error?.status === 0;
2239
+ const isMessageBounced = (message) => message.type === "error" && (message.moderation_details?.action === "MESSAGE_RESPONSE_ACTION_BOUNCE" || message.moderation?.action === "bounce");
2240
+ const isMessageBlocked = (message) => message.shadowed || message.type === "error" && (message.moderation_details?.action === "MESSAGE_RESPONSE_ACTION_REMOVE" || message.moderation?.action === "remove");
2241
+ const isMessageDeleted = (message) => Boolean(message.deleted_at || message.type === "deleted" || message.deleted_for_me);
2242
+ const isMessageEdited = (message) => !!message.message_text_updated_at;
2243
+ const hasResizeObserver = typeof window !== "undefined" && "ResizeObserver" in window;
2244
+ function autoMiddlewareFor(p) {
2245
+ if (!String(p).startsWith("auto")) return null;
2246
+ const alignment = p === "auto-start" ? "start" : p === "auto-end" ? "end" : void 0;
2247
+ return autoPlacement({ alignment });
2248
+ }
2249
+ function toOffsetMw(opt) {
2250
+ if (opt == null) return null;
2251
+ if (Array.isArray(opt)) {
2252
+ const [crossAxis, mainAxis] = opt;
2253
+ return offset({ crossAxis, mainAxis });
2254
+ }
2255
+ if (typeof opt === "number") return offset(opt);
2256
+ return offset(opt);
2257
+ }
2258
+ function usePopoverPosition({
2259
+ allowFlip = true,
2260
+ allowShift = true,
2261
+ autoUpdateOptions,
2262
+ fitAvailableSpace = false,
2263
+ freeze = false,
2264
+ offset: offset2,
2265
+ placement = "bottom-start",
2266
+ shiftOptions
2267
+ }) {
2268
+ const autoMw = autoMiddlewareFor(placement);
2269
+ const offsetMiddleware = toOffsetMw(offset2);
2270
+ const isSidePlacement = placement.startsWith("left") || placement.startsWith("right");
2271
+ const mergedShiftOptions = shiftOptions ? { padding: 8, ...shiftOptions } : { padding: 8 };
2272
+ const middleware = [
2273
+ // offset first (mirrors common Popper setups)
2274
+ ...offsetMiddleware ? [offsetMiddleware] : [],
2275
+ // choose between autoPlacement (Popper's "auto*") OR flip()
2276
+ // only allow flip when not explicitly 'left*' or 'right*'
2277
+ ...autoMw ? [autoMw] : allowFlip && !isSidePlacement ? [flip()] : [],
2278
+ // viewport collision adjustments
2279
+ ...allowShift ? [shift(mergedShiftOptions)] : [],
2280
+ // optional size constraining
2281
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
2282
+ ...fitAvailableSpace ? [size({ apply: () => {
2283
+ } })] : []
2284
+ ];
2285
+ const seedPlacement = String(placement).startsWith("auto") ? "bottom" : placement;
2286
+ return useFloating({
2287
+ middleware,
2288
+ placement: seedPlacement,
2289
+ strategy: "fixed",
2290
+ whileElementsMounted: freeze ? void 0 : (reference, floating, update) => autoUpdate(reference, floating, update, {
2291
+ ancestorResize: true,
2292
+ ancestorScroll: true,
2293
+ animationFrame: false,
2294
+ elementResize: hasResizeObserver,
2295
+ ...autoUpdateOptions
2296
+ })
2297
+ });
2298
+ }
2299
+ const LegacyThreadContext = React.createContext({ legacyThread: void 0 });
2300
+ const useLegacyThreadContext = () => useContext(LegacyThreadContext);
2301
+ const DEFAULT_PLAYBACK_RATES = [1, 1.5, 2];
2302
+ const isSeekable = (audioElement) => !(audioElement.duration === Infinity || isNaN(audioElement.duration));
2303
+ const defaultRegisterAudioPlayerError = ({
2304
+ error
2305
+ } = {}) => {
2306
+ if (!error) return;
2307
+ console.error("[AUDIO PLAYER]", error);
2308
+ };
2309
+ const elementIsPlaying = (audioElement) => audioElement && !(audioElement.paused || audioElement.ended);
2310
+ class AudioPlayer {
2311
+ constructor({
2312
+ durationSeconds,
2313
+ fileSize,
2314
+ id,
2315
+ mimeType,
2316
+ playbackRates: customPlaybackRates,
2317
+ plugins,
2318
+ pool,
2319
+ src,
2320
+ title,
2321
+ waveformData
2322
+ }) {
2323
+ this._plugins = /* @__PURE__ */ new Map();
2324
+ this.playTimeout = void 0;
2325
+ this.unsubscribeEventListeners = null;
2326
+ this._disposed = false;
2327
+ this._metadataProbe = null;
2328
+ this._restoringPosition = false;
2329
+ this._removalTimeout = void 0;
2330
+ this.setDurationSeconds = (durationSeconds2) => {
2331
+ this._data.durationSeconds = durationSeconds2;
2332
+ this.state.partialNext({ durationSeconds: durationSeconds2 });
2333
+ };
2334
+ this.setPlaybackStartSafetyTimeout = () => {
2335
+ clearTimeout(this.playTimeout);
2336
+ this.playTimeout = setTimeout(() => {
2337
+ if (!this.elementRef) return;
2338
+ try {
2339
+ this.elementRef.pause();
2340
+ this.state.partialNext({ isPlaying: false });
2341
+ } catch (e) {
2342
+ this.registerError({ errCode: "failed-to-start" });
2343
+ }
2344
+ }, 2e3);
2345
+ };
2346
+ this.updateDurationFromElement = (element) => {
2347
+ const duration = element.duration;
2348
+ if (typeof duration !== "number" || isNaN(duration) || !isFinite(duration) || duration <= 0) {
2349
+ return;
2350
+ }
2351
+ this.setDurationSeconds(duration);
2352
+ };
2353
+ this.clearMetadataProbe = () => {
2354
+ const probe = this._metadataProbe;
2355
+ this._metadataProbe = null;
2356
+ this._metadataProbePromise = void 0;
2357
+ if (!probe) return;
2358
+ try {
2359
+ probe.pause();
2360
+ } catch {
2361
+ }
2362
+ probe.removeAttribute("src");
2363
+ try {
2364
+ probe.load();
2365
+ } catch {
2366
+ }
2367
+ };
2368
+ this.preloadMetadata = () => {
2369
+ if (this._disposed || this.durationSeconds != null || !this.src || this._metadataProbePromise || typeof document === "undefined") {
2370
+ return;
2371
+ }
2372
+ const probe = document.createElement("audio");
2373
+ probe.preload = "metadata";
2374
+ this._metadataProbe = probe;
2375
+ this._metadataProbePromise = new Promise((resolve) => {
2376
+ const cleanup = () => {
2377
+ probe.removeEventListener("loadedmetadata", handleLoadedMetadata);
2378
+ probe.removeEventListener("error", handleError);
2379
+ if (this._metadataProbe === probe) {
2380
+ this.clearMetadataProbe();
2381
+ } else {
2382
+ this._metadataProbePromise = void 0;
2383
+ }
2384
+ resolve();
2385
+ };
2386
+ const handleLoadedMetadata = () => {
2387
+ this.updateDurationFromElement(probe);
2388
+ cleanup();
2389
+ };
2390
+ const handleError = () => {
2391
+ cleanup();
2392
+ };
2393
+ probe.addEventListener("loadedmetadata", handleLoadedMetadata, { once: true });
2394
+ probe.addEventListener("error", handleError, { once: true });
2395
+ probe.src = this.src;
2396
+ try {
2397
+ probe.load();
2398
+ } catch {
2399
+ cleanup();
2400
+ }
2401
+ });
2402
+ };
2403
+ this.clearPlaybackStartSafetyTimeout = () => {
2404
+ if (!this.elementRef) return;
2405
+ clearTimeout(this.playTimeout);
2406
+ this.playTimeout = void 0;
2407
+ };
2408
+ this.clearPendingLoadedMeta = () => {
2409
+ const pending = this._pendingLoadedMeta;
2410
+ if (pending?.element && pending.onLoaded) {
2411
+ pending.element.removeEventListener("loadedmetadata", pending.onLoaded);
2412
+ }
2413
+ this._pendingLoadedMeta = void 0;
2414
+ };
2415
+ this.restoreSavedPosition = (elementRef) => {
2416
+ const saved = this.secondsElapsed;
2417
+ if (!saved || saved <= 0) return;
2418
+ const apply = () => {
2419
+ const duration = elementRef.duration;
2420
+ const clamped = typeof duration === "number" && !isNaN(duration) && isFinite(duration) ? Math.min(saved, duration) : saved;
2421
+ try {
2422
+ if (elementRef.currentTime === clamped) return;
2423
+ elementRef.currentTime = clamped;
2424
+ this.setSecondsElapsed(clamped);
2425
+ } catch {
2426
+ }
2427
+ };
2428
+ if (elementRef.readyState < 1) {
2429
+ this.clearPendingLoadedMeta();
2430
+ this._restoringPosition = true;
2431
+ const onLoaded = () => {
2432
+ if (this._pendingLoadedMeta?.onLoaded !== onLoaded) return;
2433
+ this._pendingLoadedMeta = void 0;
2434
+ if (this.elementRef !== elementRef) {
2435
+ this._restoringPosition = false;
2436
+ return;
2437
+ }
2438
+ apply();
2439
+ this._restoringPosition = false;
2440
+ };
2441
+ elementRef.addEventListener("loadedmetadata", onLoaded, { once: true });
2442
+ this._pendingLoadedMeta = { element: elementRef, onLoaded };
2443
+ } else {
2444
+ this._restoringPosition = true;
2445
+ apply();
2446
+ this._restoringPosition = false;
2447
+ }
2448
+ };
2449
+ this.elementIsReady = () => {
2450
+ if (this._elementIsReadyPromise) return this._elementIsReadyPromise;
2451
+ this._elementIsReadyPromise = new Promise((resolve) => {
2452
+ if (!this.elementRef) return resolve(false);
2453
+ const element = this.elementRef;
2454
+ const handleLoaded = () => {
2455
+ element.removeEventListener("loadedmetadata", handleLoaded);
2456
+ resolve(element.readyState > 0);
2457
+ };
2458
+ element.addEventListener("loadedmetadata", handleLoaded);
2459
+ });
2460
+ return this._elementIsReadyPromise;
2461
+ };
2462
+ this.setRef = (elementRef) => {
2463
+ if (elementIsPlaying(this.elementRef)) {
2464
+ this.releaseElement({ resetState: false });
2465
+ }
2466
+ this.clearPendingLoadedMeta();
2467
+ this.clearMetadataProbe();
2468
+ this._restoringPosition = false;
2469
+ this._elementIsReadyPromise = void 0;
2470
+ this.state.partialNext({ elementRef });
2471
+ if (elementRef) {
2472
+ this.registerSubscriptions();
2473
+ }
2474
+ };
2475
+ this.setSecondsElapsed = (secondsElapsed) => {
2476
+ const duration = this.elementRef?.duration ?? this.durationSeconds;
2477
+ this.state.partialNext({
2478
+ progressPercent: duration && secondsElapsed ? secondsElapsed / duration * 100 : 0,
2479
+ secondsElapsed
2480
+ });
2481
+ };
2482
+ this.canPlayMimeType = (mimeType2) => {
2483
+ if (!mimeType2) return false;
2484
+ if (this.elementRef) return !!this.elementRef.canPlayType(mimeType2);
2485
+ return !!new Audio().canPlayType(mimeType2);
2486
+ };
2487
+ this.play = async (params) => {
2488
+ if (this._disposed) return;
2489
+ const elementRef = this.ensureElementRef();
2490
+ if (elementIsPlaying(this.elementRef)) {
2491
+ if (this.isPlaying) return;
2492
+ this.state.partialNext({ isPlaying: true });
2493
+ return;
2494
+ }
2495
+ const { currentPlaybackRate, playbackRates: playbackRates2 } = {
2496
+ currentPlaybackRate: this.currentPlaybackRate,
2497
+ playbackRates: this.playbackRates,
2498
+ ...params
2499
+ };
2500
+ if (!this.canPlayRecord) {
2501
+ this.registerError({ errCode: "not-playable" });
2502
+ return;
2503
+ }
2504
+ this.restoreSavedPosition(elementRef);
2505
+ elementRef.playbackRate = currentPlaybackRate ?? this.currentPlaybackRate;
2506
+ this.setPlaybackStartSafetyTimeout();
2507
+ try {
2508
+ await elementRef.play();
2509
+ this.state.partialNext({
2510
+ currentPlaybackRate,
2511
+ isPlaying: true,
2512
+ playbackRates: playbackRates2
2513
+ });
2514
+ this._pool.setActiveAudioPlayer(this);
2515
+ } catch (e) {
2516
+ this.registerError({ error: e });
2517
+ this.state.partialNext({ isPlaying: false });
2518
+ } finally {
2519
+ this.clearPlaybackStartSafetyTimeout();
2520
+ }
2521
+ };
2522
+ this.pause = () => {
2523
+ if (!elementIsPlaying(this.elementRef)) return;
2524
+ this.clearPlaybackStartSafetyTimeout();
2525
+ this.elementRef.pause();
2526
+ this.state.partialNext({ isPlaying: false });
2527
+ };
2528
+ this.stop = () => {
2529
+ this.pause();
2530
+ this.state.partialNext({ isPlaying: false });
2531
+ this.setSecondsElapsed(0);
2532
+ if (this.elementRef) this.elementRef.currentTime = 0;
2533
+ };
2534
+ this.togglePlay = async () => this.isPlaying ? this.pause() : await this.play();
2535
+ this.increasePlaybackRate = () => {
2536
+ let currentPlaybackRateIndex = this.state.getLatestValue().playbackRates.findIndex((rate) => rate === this.currentPlaybackRate);
2537
+ if (currentPlaybackRateIndex === -1) {
2538
+ currentPlaybackRateIndex = 0;
2539
+ }
2540
+ const nextIndex = currentPlaybackRateIndex === this.playbackRates.length - 1 ? 0 : currentPlaybackRateIndex + 1;
2541
+ const currentPlaybackRate = this.playbackRates[nextIndex];
2542
+ this.state.partialNext({ currentPlaybackRate });
2543
+ if (this.elementRef) {
2544
+ this.elementRef.playbackRate = currentPlaybackRate;
2545
+ }
2546
+ };
2547
+ this.seek = throttle(async ({ clientX, currentTarget }) => {
2548
+ let element = this.elementRef;
2549
+ if (!this.elementRef) {
2550
+ element = this.ensureElementRef();
2551
+ const isReady = await this.elementIsReady();
2552
+ if (!isReady) return;
2553
+ }
2554
+ if (!currentTarget || !element) return;
2555
+ if (!isSeekable(element)) {
2556
+ this.registerError({ errCode: "seek-not-supported" });
2557
+ return;
2558
+ }
2559
+ const { width, x } = currentTarget.getBoundingClientRect();
2560
+ const ratio = (clientX - x) / width;
2561
+ if (ratio > 1 || ratio < 0) return;
2562
+ const currentTime = ratio * element.duration;
2563
+ this.setSecondsElapsed(currentTime);
2564
+ element.currentTime = currentTime;
2565
+ }, 16);
2566
+ this.registerError = (params) => {
2567
+ defaultRegisterAudioPlayerError(params);
2568
+ this.plugins.forEach(({ onError }) => onError?.({ player: this, ...params }));
2569
+ };
2570
+ this.requestRemoval = () => {
2571
+ this._disposed = true;
2572
+ this.cancelScheduledRemoval();
2573
+ this.clearPendingLoadedMeta();
2574
+ this.clearMetadataProbe();
2575
+ this._restoringPosition = false;
2576
+ this.releaseElement({ resetState: true });
2577
+ this.unsubscribeEventListeners?.();
2578
+ this.unsubscribeEventListeners = null;
2579
+ this.plugins.forEach(({ onRemove }) => onRemove?.({ player: this }));
2580
+ this._pool.deregister(this.id);
2581
+ };
2582
+ this.cancelScheduledRemoval = () => {
2583
+ clearTimeout(this._removalTimeout);
2584
+ this._removalTimeout = void 0;
2585
+ };
2586
+ this.scheduleRemoval = (ms = 0) => {
2587
+ this.cancelScheduledRemoval();
2588
+ this._removalTimeout = setTimeout(() => {
2589
+ if (this.disposed) return;
2590
+ this.requestRemoval();
2591
+ }, ms);
2592
+ };
2593
+ this.releaseElementForHandoff = () => {
2594
+ if (!this.elementRef) return;
2595
+ this.releaseElement({ resetState: false });
2596
+ this.unsubscribeEventListeners?.();
2597
+ this.unsubscribeEventListeners = null;
2598
+ };
2599
+ this.registerSubscriptions = () => {
2600
+ this.unsubscribeEventListeners?.();
2601
+ const audioElement = this.elementRef;
2602
+ if (!audioElement) return;
2603
+ const handleEnded = () => {
2604
+ if (audioElement) {
2605
+ this.updateDurationFromElement(audioElement);
2606
+ }
2607
+ this.stop();
2608
+ };
2609
+ const handleError = (e) => {
2610
+ const audio = e.currentTarget;
2611
+ const state = { isPlaying: false };
2612
+ if (!audio?.error?.code) {
2613
+ this.state.partialNext(state);
2614
+ return;
2615
+ }
2616
+ if (audio.error.code === 4) {
2617
+ state.canPlayRecord = false;
2618
+ this.state.partialNext(state);
2619
+ }
2620
+ const errorMsg = [
2621
+ void 0,
2622
+ "MEDIA_ERR_ABORTED: fetch aborted by user",
2623
+ "MEDIA_ERR_NETWORK: network failed while fetching",
2624
+ "MEDIA_ERR_DECODE: audio fetched but couldn’t decode",
2625
+ "MEDIA_ERR_SRC_NOT_SUPPORTED: source not supported"
2626
+ ][audio?.error?.code];
2627
+ if (!errorMsg) return;
2628
+ defaultRegisterAudioPlayerError({ error: new Error(errorMsg + ` (${audio.src})`) });
2629
+ };
2630
+ const handleTimeupdate = () => {
2631
+ const t = audioElement?.currentTime ?? 0;
2632
+ if (this._restoringPosition && t === 0) return;
2633
+ if (!this.isPlaying && t === 0 && this.secondsElapsed > 0) return;
2634
+ this.setSecondsElapsed(t);
2635
+ };
2636
+ const handleLoadedMetadata = () => {
2637
+ if (audioElement) {
2638
+ this.updateDurationFromElement(audioElement);
2639
+ }
2640
+ };
2641
+ audioElement.addEventListener("ended", handleEnded);
2642
+ audioElement.addEventListener("error", handleError);
2643
+ audioElement.addEventListener("loadedmetadata", handleLoadedMetadata);
2644
+ audioElement.addEventListener("timeupdate", handleTimeupdate);
2645
+ this.unsubscribeEventListeners = () => {
2646
+ audioElement.pause();
2647
+ audioElement.removeEventListener("ended", handleEnded);
2648
+ audioElement.removeEventListener("error", handleError);
2649
+ audioElement.removeEventListener("loadedmetadata", handleLoadedMetadata);
2650
+ audioElement.removeEventListener("timeupdate", handleTimeupdate);
2651
+ };
2652
+ };
2653
+ this._data = {
2654
+ durationSeconds,
2655
+ fileSize,
2656
+ id,
2657
+ mimeType,
2658
+ src,
2659
+ title,
2660
+ waveformData
2661
+ };
2662
+ this._pool = pool;
2663
+ this.setPlugins(() => plugins ?? []);
2664
+ const playbackRates = customPlaybackRates?.length ? customPlaybackRates : DEFAULT_PLAYBACK_RATES;
2665
+ const canPlayRecord = mimeType ? !!new Audio().canPlayType(mimeType) : true;
2666
+ this.state = new StateStore({
2667
+ canPlayRecord,
2668
+ currentPlaybackRate: playbackRates[0],
2669
+ durationSeconds,
2670
+ elementRef: null,
2671
+ isPlaying: false,
2672
+ playbackError: null,
2673
+ playbackRates,
2674
+ progressPercent: 0,
2675
+ secondsElapsed: 0
2676
+ });
2677
+ this.plugins.forEach((p) => p.onInit?.({ player: this }));
2678
+ this.preloadMetadata();
2722
2679
  }
2723
- if (messageActions.indexOf(MESSAGE_ACTIONS.download) > -1) {
2724
- messageActionsAfterPermission.push(MESSAGE_ACTIONS.download);
2680
+ get plugins() {
2681
+ return Array.from(this._plugins.values());
2725
2682
  }
2726
- if (canDelete && messageActions.indexOf(OPTIONAL_MESSAGE_ACTIONS.deleteForMe) > -1) {
2727
- messageActionsAfterPermission.push(OPTIONAL_MESSAGE_ACTIONS.deleteForMe);
2683
+ get canPlayRecord() {
2684
+ return this.state.getLatestValue().canPlayRecord;
2728
2685
  }
2729
- if (canEdit && messageActions.indexOf(MESSAGE_ACTIONS.edit) > -1) {
2730
- messageActionsAfterPermission.push(MESSAGE_ACTIONS.edit);
2686
+ get elementRef() {
2687
+ return this.state.getLatestValue().elementRef;
2731
2688
  }
2732
- if (canFlag && messageActions.indexOf(MESSAGE_ACTIONS.flag) > -1) {
2733
- messageActionsAfterPermission.push(MESSAGE_ACTIONS.flag);
2689
+ get isPlaying() {
2690
+ return this.state.getLatestValue().isPlaying;
2734
2691
  }
2735
- if (canMarkUnread && messageActions.indexOf(MESSAGE_ACTIONS.markUnread) > -1) {
2736
- messageActionsAfterPermission.push(MESSAGE_ACTIONS.markUnread);
2692
+ get currentPlaybackRate() {
2693
+ return this.state.getLatestValue().currentPlaybackRate;
2737
2694
  }
2738
- if (canMute && messageActions.indexOf(MESSAGE_ACTIONS.mute) > -1) {
2739
- messageActionsAfterPermission.push(MESSAGE_ACTIONS.mute);
2695
+ get playbackRates() {
2696
+ return this.state.getLatestValue().playbackRates;
2740
2697
  }
2741
- if (canPin && messageActions.indexOf(MESSAGE_ACTIONS.pin) > -1) {
2742
- messageActionsAfterPermission.push(MESSAGE_ACTIONS.pin);
2698
+ get durationSeconds() {
2699
+ return this.state.getLatestValue().durationSeconds;
2743
2700
  }
2744
- if (canQuote && messageActions.indexOf(MESSAGE_ACTIONS.quote) > -1) {
2745
- messageActionsAfterPermission.push(MESSAGE_ACTIONS.quote);
2701
+ get fileSize() {
2702
+ return this._data.fileSize;
2746
2703
  }
2747
- if (canReact && messageActions.indexOf(MESSAGE_ACTIONS.react) > -1) {
2748
- messageActionsAfterPermission.push(MESSAGE_ACTIONS.react);
2704
+ get id() {
2705
+ return this._data.id;
2749
2706
  }
2750
- if (channelConfig?.["user_message_reminders"] && messageActions.indexOf(MESSAGE_ACTIONS.remindMe) > -1) {
2751
- messageActionsAfterPermission.push(MESSAGE_ACTIONS.remindMe);
2707
+ get src() {
2708
+ return this._data.src;
2752
2709
  }
2753
- if (canReply && messageActions.indexOf(MESSAGE_ACTIONS.reply) > -1) {
2754
- messageActionsAfterPermission.push(MESSAGE_ACTIONS.reply);
2710
+ get mimeType() {
2711
+ return this._data.mimeType;
2755
2712
  }
2756
- if (channelConfig?.["user_message_reminders"] && messageActions.indexOf(MESSAGE_ACTIONS.saveForLater) > -1) {
2757
- messageActionsAfterPermission.push(MESSAGE_ACTIONS.saveForLater);
2713
+ get title() {
2714
+ return this._data.title;
2758
2715
  }
2759
- return messageActionsAfterPermission;
2760
- };
2761
- const ACTIONS_NOT_WORKING_IN_THREAD = [
2762
- MESSAGE_ACTIONS.pin,
2763
- MESSAGE_ACTIONS.reply,
2764
- MESSAGE_ACTIONS.markUnread
2765
- ];
2766
- function areMessagesEqual(prevMessage, nextMessage) {
2767
- const areBaseMessagesEqual = (prevMessage2, nextMessage2) => prevMessage2.deleted_at === nextMessage2.deleted_at && prevMessage2.latest_reactions?.length === nextMessage2.latest_reactions?.length && prevMessage2.own_reactions?.length === nextMessage2.own_reactions?.length && prevMessage2.pinned === nextMessage2.pinned && prevMessage2.reply_count === nextMessage2.reply_count && prevMessage2.show_in_channel === nextMessage2.show_in_channel && prevMessage2.status === nextMessage2.status && prevMessage2.text === nextMessage2.text && prevMessage2.type === nextMessage2.type && prevMessage2.updated_at === nextMessage2.updated_at && prevMessage2.user?.updated_at === nextMessage2.user?.updated_at;
2768
- return areBaseMessagesEqual(prevMessage, nextMessage) && Boolean(prevMessage.quoted_message) === Boolean(nextMessage.quoted_message) && (!prevMessage.quoted_message && !nextMessage.quoted_message || areBaseMessagesEqual(
2769
- prevMessage.quoted_message,
2770
- nextMessage.quoted_message
2771
- ));
2772
- }
2773
- const areMessagePropsEqual = (prevProps, nextProps) => {
2774
- const { message: prevMessage, Message: prevMessageUI } = prevProps;
2775
- const { message: nextMessage, Message: nextMessageUI } = nextProps;
2776
- if (prevMessageUI !== nextMessageUI) return false;
2777
- if (nextProps.showDetailedReactions !== prevProps.showDetailedReactions) {
2778
- return false;
2716
+ get waveformData() {
2717
+ return this._data.waveformData;
2779
2718
  }
2780
- if (nextProps.closeReactionSelectorOnClick !== prevProps.closeReactionSelectorOnClick) {
2781
- return false;
2719
+ get secondsElapsed() {
2720
+ return this.state.getLatestValue().secondsElapsed;
2782
2721
  }
2783
- const messagesAreEqual = areMessagesEqual(prevMessage, nextMessage);
2784
- if (!messagesAreEqual) return false;
2785
- const deepEqualProps = deepequal(nextProps.messageActions, prevProps.messageActions) && deepequal(nextProps.readBy, prevProps.readBy) && deepequal(nextProps.deliveredTo, prevProps.deliveredTo) && deepequal(nextProps.highlighted, prevProps.highlighted) && deepequal(nextProps.groupStyles, prevProps.groupStyles) && // last 3 messages can have different group styles
2786
- deepequal(nextProps.mutes, prevProps.mutes) && deepequal(nextProps.lastReceivedId, prevProps.lastReceivedId);
2787
- if (!deepEqualProps) return false;
2788
- return prevProps.messageListRect === nextProps.messageListRect;
2789
- };
2790
- const areMessageUIPropsEqual = (prevProps, nextProps) => {
2791
- const { lastReceivedId: prevLastReceivedId, message: prevMessage } = prevProps;
2792
- const { lastReceivedId: nextLastReceivedId, message: nextMessage } = nextProps;
2793
- if (prevProps.highlighted !== nextProps.highlighted) return false;
2794
- if (prevProps.threadList !== nextProps.threadList) return false;
2795
- if (prevProps.endOfGroup !== nextProps.endOfGroup) return false;
2796
- if (prevProps.mutes?.length !== nextProps.mutes?.length) return false;
2797
- if (prevProps.readBy?.length !== nextProps.readBy?.length) return false;
2798
- if (prevProps.deliveredTo?.length !== nextProps.deliveredTo?.length) return false;
2799
- if (prevProps.groupStyles !== nextProps.groupStyles) return false;
2800
- if (prevProps.showDetailedReactions !== nextProps.showDetailedReactions) {
2801
- return false;
2722
+ get progressPercent() {
2723
+ return this.state.getLatestValue().progressPercent;
2802
2724
  }
2803
- if ((prevMessage.id === prevLastReceivedId || prevMessage.id === nextLastReceivedId) && prevLastReceivedId !== nextLastReceivedId) {
2804
- return false;
2725
+ get disposed() {
2726
+ return this._disposed;
2805
2727
  }
2806
- return areMessagesEqual(prevMessage, nextMessage);
2807
- };
2808
- const messageHasReactions = (message) => Object.values(message?.reaction_groups ?? {}).some(({ count }) => count > 0);
2809
- const messageHasQuotedMessage = (message) => !!message?.quoted_message;
2810
- const messageHasAttachments = (message) => !!message?.attachments && !!message.attachments.length;
2811
- const messageHasSingleAttachment = (message) => message?.attachments?.length === 1;
2812
- const messageHasGiphyAttachment = (message) => !!message?.attachments?.some((att) => att.type === "giphy");
2813
- const getImages = (message) => {
2814
- if (!message?.attachments) {
2815
- return [];
2728
+ ensureElementRef() {
2729
+ if (this._disposed) {
2730
+ throw new Error("AudioPlayer is disposed");
2731
+ }
2732
+ if (!this.elementRef) {
2733
+ const el = this._pool.acquireElement({
2734
+ ownerId: this.id,
2735
+ src: this.src
2736
+ });
2737
+ this.setRef(el);
2738
+ }
2739
+ return this.elementRef;
2816
2740
  }
2817
- return message.attachments.filter((item) => item.type === "image");
2818
- };
2819
- const getNonImageAttachments = (message) => {
2820
- if (!message?.attachments) {
2821
- return [];
2741
+ setDescriptor(descriptor) {
2742
+ const previousSrc = this.src;
2743
+ this._data = { ...this._data, ...descriptor };
2744
+ if (descriptor.src !== previousSrc && this.elementRef) {
2745
+ this.elementRef.src = descriptor.src;
2746
+ }
2747
+ if (descriptor.src && descriptor.src !== previousSrc) {
2748
+ this.clearMetadataProbe();
2749
+ if (descriptor.durationSeconds == null) {
2750
+ this.setDurationSeconds(void 0);
2751
+ this.preloadMetadata();
2752
+ } else {
2753
+ this.setDurationSeconds(descriptor.durationSeconds);
2754
+ }
2755
+ return;
2756
+ }
2757
+ if (descriptor.durationSeconds != null) {
2758
+ this.setDurationSeconds(descriptor.durationSeconds);
2759
+ }
2822
2760
  }
2823
- return message.attachments.filter((item) => item.type !== "image");
2824
- };
2825
- const mapToUserNameOrId = (user) => user.name || user.id;
2826
- const getReadByTooltipText = (users, t, client, tooltipUserNameMapper) => {
2827
- let outStr = "";
2828
- if (!t) {
2829
- throw new Error(
2830
- "getReadByTooltipText was called, but translation function is not available"
2831
- );
2761
+ releaseElement({ resetState }) {
2762
+ this.clearPendingLoadedMeta();
2763
+ this.clearMetadataProbe();
2764
+ this._restoringPosition = false;
2765
+ if (resetState) {
2766
+ this.stop();
2767
+ } else {
2768
+ this.state.partialNext({ isPlaying: false });
2769
+ if (this.elementRef) {
2770
+ try {
2771
+ this.elementRef.pause();
2772
+ } catch {
2773
+ }
2774
+ }
2775
+ }
2776
+ if (this.elementRef) {
2777
+ this._pool.releaseElement(this.id);
2778
+ this.setRef(null);
2779
+ }
2832
2780
  }
2833
- if (!tooltipUserNameMapper) {
2834
- throw new Error(
2835
- "getReadByTooltipText was called, but tooltipUserNameMapper function is not available"
2836
- );
2781
+ setPlugins(setter) {
2782
+ this._plugins = setter(this.plugins).reduce((acc, plugin) => {
2783
+ if (plugin.id) {
2784
+ acc.set(plugin.id, plugin);
2785
+ }
2786
+ return acc;
2787
+ }, /* @__PURE__ */ new Map());
2837
2788
  }
2838
- const otherUsers = users.filter((item) => item && client?.user && item.id !== client.user.id).map(tooltipUserNameMapper);
2839
- const slicedArr = otherUsers.slice(0, 5);
2840
- const restLength = otherUsers.length - slicedArr.length;
2841
- if (slicedArr.length === 1) {
2842
- outStr = `${slicedArr[0]} `;
2843
- } else if (slicedArr.length === 2) {
2844
- outStr = t("{{ firstUser }} and {{ secondUser }}", {
2845
- firstUser: slicedArr[0],
2846
- secondUser: slicedArr[1]
2789
+ }
2790
+ class AudioPlayerPool {
2791
+ constructor(config) {
2792
+ this.state = new StateStore({
2793
+ activeAudioPlayer: null
2847
2794
  });
2848
- } else if (slicedArr.length > 2) {
2849
- if (restLength === 0) {
2850
- const lastUser = slicedArr.splice(slicedArr.length - 1, 1);
2851
- outStr = t("{{ commaSeparatedUsers }}, and {{ lastUser }}", {
2852
- commaSeparatedUsers: slicedArr.join(", "),
2853
- lastUser
2795
+ this.pool = /* @__PURE__ */ new Map();
2796
+ this.audios = /* @__PURE__ */ new Map();
2797
+ this.sharedAudio = null;
2798
+ this.sharedOwnerId = null;
2799
+ this.getOrAdd = (params) => {
2800
+ const { playbackRates, plugins, ...descriptor } = params;
2801
+ let player = this.pool.get(params.id);
2802
+ if (player) {
2803
+ if (!player.disposed) {
2804
+ player.setDescriptor(descriptor);
2805
+ return player;
2806
+ }
2807
+ this.deregister(params.id);
2808
+ }
2809
+ player = new AudioPlayer({
2810
+ playbackRates,
2811
+ plugins,
2812
+ ...descriptor,
2813
+ pool: this
2854
2814
  });
2855
- } else {
2856
- outStr = t("{{ commaSeparatedUsers }} and {{ moreCount }} more", {
2857
- commaSeparatedUsers: slicedArr.join(", "),
2858
- moreCount: restLength
2815
+ this.pool.set(params.id, player);
2816
+ return player;
2817
+ };
2818
+ this.acquireElement = ({ ownerId, src }) => {
2819
+ if (!this.allowConcurrentPlayback) {
2820
+ if (!this.sharedAudio) {
2821
+ this.sharedAudio = new Audio();
2822
+ }
2823
+ if (this.sharedOwnerId && this.sharedOwnerId !== ownerId) {
2824
+ const previous = this.pool.get(this.sharedOwnerId);
2825
+ previous?.pause();
2826
+ previous?.releaseElementForHandoff();
2827
+ }
2828
+ this.sharedOwnerId = ownerId;
2829
+ if (this.sharedAudio.src !== src) {
2830
+ this.sharedAudio.src = src;
2831
+ }
2832
+ return this.sharedAudio;
2833
+ }
2834
+ let audio = this.audios.get(ownerId);
2835
+ if (!audio) {
2836
+ audio = new Audio();
2837
+ this.audios.set(ownerId, audio);
2838
+ }
2839
+ if (audio.src !== src) {
2840
+ audio.src = src;
2841
+ }
2842
+ return audio;
2843
+ };
2844
+ this.releaseElement = (ownerId) => {
2845
+ if (!this.allowConcurrentPlayback) {
2846
+ if (this.sharedOwnerId !== ownerId) return;
2847
+ const el2 = this.sharedAudio;
2848
+ if (el2) {
2849
+ try {
2850
+ el2.pause();
2851
+ } catch {
2852
+ }
2853
+ el2.removeAttribute("src");
2854
+ el2.load();
2855
+ }
2856
+ this.sharedOwnerId = null;
2857
+ return;
2858
+ }
2859
+ const el = this.audios.get(ownerId);
2860
+ if (!el) return;
2861
+ try {
2862
+ el.pause();
2863
+ } catch {
2864
+ }
2865
+ el.removeAttribute("src");
2866
+ el.load();
2867
+ this.audios.delete(ownerId);
2868
+ };
2869
+ this.setActiveAudioPlayer = (activeAudioPlayer) => {
2870
+ if (this.allowConcurrentPlayback) return;
2871
+ this.state.partialNext({ activeAudioPlayer });
2872
+ };
2873
+ this.remove = (id) => {
2874
+ const player = this.pool.get(id);
2875
+ if (!player) return;
2876
+ player.requestRemoval();
2877
+ };
2878
+ this.clear = () => {
2879
+ this.players.forEach((player) => {
2880
+ this.remove(player.id);
2881
+ });
2882
+ };
2883
+ this.registerSubscriptions = () => {
2884
+ this.players.forEach((p) => {
2885
+ if (p.elementRef) {
2886
+ p.registerSubscriptions();
2887
+ }
2888
+ });
2889
+ };
2890
+ this.allowConcurrentPlayback = !!config?.allowConcurrentPlayback;
2891
+ }
2892
+ get players() {
2893
+ return Array.from(this.pool.values());
2894
+ }
2895
+ get activeAudioPlayer() {
2896
+ return this.state.getLatestValue().activeAudioPlayer;
2897
+ }
2898
+ /** Removes the AudioPlayer instance from the pool of players */
2899
+ deregister(id) {
2900
+ if (this.pool.has(id)) {
2901
+ this.pool.delete(id);
2902
+ }
2903
+ if (this.activeAudioPlayer?.id === id) {
2904
+ this.setActiveAudioPlayer(null);
2905
+ }
2906
+ }
2907
+ }
2908
+ const SEEK_NOT_SUPPORTED_NOTIFICATION_DEBOUNCE_INTERVAL_MS = 1e3;
2909
+ const audioPlayerNotificationsPluginFactory = ({
2910
+ addNotification,
2911
+ panel = "channel",
2912
+ t
2913
+ }) => {
2914
+ const errors = {
2915
+ "failed-to-start": new Error(t("Failed to play the recording")),
2916
+ "not-playable": new Error(
2917
+ t("Recording format is not supported and cannot be reproduced")
2918
+ ),
2919
+ "seek-not-supported": new Error(t("Cannot seek in the recording"))
2920
+ };
2921
+ let lastSeekNotSupportedNotificationAt;
2922
+ return {
2923
+ id: "AudioPlayerNotificationsPlugin",
2924
+ onError: ({ errCode, error: e }) => {
2925
+ if (errCode === "seek-not-supported") {
2926
+ const now = Date.now();
2927
+ if (typeof lastSeekNotSupportedNotificationAt === "number" && now - lastSeekNotSupportedNotificationAt < SEEK_NOT_SUPPORTED_NOTIFICATION_DEBOUNCE_INTERVAL_MS) {
2928
+ return;
2929
+ }
2930
+ lastSeekNotSupportedNotificationAt = now;
2931
+ }
2932
+ const error = (errCode && errors[errCode]) ?? e ?? new Error(t("Error reproducing the recording"));
2933
+ addNotification({
2934
+ emitter: "AudioPlayer",
2935
+ error,
2936
+ message: error.message,
2937
+ severity: "error",
2938
+ targetPanels: [panel],
2939
+ type: "browser:audio:playback:error"
2859
2940
  });
2860
2941
  }
2861
- }
2862
- return outStr;
2942
+ };
2863
2943
  };
2864
- const countEmojis = (text) => {
2865
- const matches = text?.match(emojiRegex());
2866
- return matches ? matches.length : 0;
2944
+ const AudioPlayerContext = React.createContext({
2945
+ audioPlayers: null
2946
+ });
2947
+ const WithAudioPlayback = ({
2948
+ allowConcurrentPlayback,
2949
+ children
2950
+ }) => {
2951
+ const [audioPlayers] = useState(() => new AudioPlayerPool({ allowConcurrentPlayback }));
2952
+ useEffect(
2953
+ () => () => {
2954
+ audioPlayers.clear();
2955
+ },
2956
+ [audioPlayers]
2957
+ );
2958
+ return /* @__PURE__ */ jsx(AudioPlayerContext.Provider, { value: { audioPlayers }, children });
2867
2959
  };
2868
- const messageTextHasEmojisOnly = (message) => {
2869
- if (!message.text) return false;
2870
- const noEmojis = message.text.replace(emojiRegex(), "");
2871
- const noSpace = noEmojis.replace(/[\s\n]/gm, "");
2872
- return !noSpace;
2960
+ const makeAudioPlayerId = ({ requester, src }) => `${requester ?? "requester-unknown"}:${src}`;
2961
+ const useAudioPlayer = ({
2962
+ durationSeconds,
2963
+ fileSize,
2964
+ mimeType,
2965
+ playbackRates,
2966
+ plugins,
2967
+ requester = "",
2968
+ src,
2969
+ title,
2970
+ waveformData
2971
+ }) => {
2972
+ const { addNotification } = useNotificationApi();
2973
+ const panel = useNotificationTarget();
2974
+ const { t } = useTranslationContext();
2975
+ const { audioPlayers } = useContext(AudioPlayerContext);
2976
+ const audioPlayer = src && audioPlayers ? audioPlayers.getOrAdd({
2977
+ durationSeconds,
2978
+ fileSize,
2979
+ id: makeAudioPlayerId({ requester, src }),
2980
+ mimeType,
2981
+ playbackRates,
2982
+ plugins,
2983
+ src,
2984
+ title,
2985
+ waveformData
2986
+ }) : void 0;
2987
+ useEffect(() => {
2988
+ if (!audioPlayer) return;
2989
+ const notificationsPlugin = audioPlayerNotificationsPluginFactory({
2990
+ addNotification,
2991
+ panel,
2992
+ t
2993
+ });
2994
+ audioPlayer.setPlugins((currentPlugins) => [
2995
+ ...currentPlugins.filter((plugin) => plugin.id !== notificationsPlugin.id),
2996
+ notificationsPlugin
2997
+ ]);
2998
+ }, [addNotification, audioPlayer, panel, t]);
2999
+ return audioPlayer;
3000
+ };
3001
+ const activeAudioPlayerSelector = ({ activeAudioPlayer }) => ({
3002
+ activeAudioPlayer
3003
+ });
3004
+ const useActiveAudioPlayer = () => {
3005
+ const { audioPlayers } = useContext(AudioPlayerContext);
3006
+ const { activeAudioPlayer } = useStateStore(audioPlayers?.state, activeAudioPlayerSelector) ?? {};
3007
+ return activeAudioPlayer;
2873
3008
  };
2874
- const isMessageErrorRetryable = (message) => message.status === "failed" && message.error?.status !== 403;
2875
- const isNetworkSendFailure = (message) => message.status === "failed" && message.error?.status === 0;
2876
- const isMessageBounced = (message) => message.type === "error" && (message.moderation_details?.action === "MESSAGE_RESPONSE_ACTION_BOUNCE" || message.moderation?.action === "bounce");
2877
- const isMessageBlocked = (message) => message.type === "error" && (message.moderation_details?.action === "MESSAGE_RESPONSE_ACTION_REMOVE" || message.moderation?.action === "remove");
2878
- const isMessageDeleted = (message) => Boolean(message.deleted_at || message.type === "deleted" || message.deleted_for_me);
2879
- const isMessageEdited = (message) => !!message.message_text_updated_at;
2880
- const hasResizeObserver = typeof window !== "undefined" && "ResizeObserver" in window;
2881
- function autoMiddlewareFor(p) {
2882
- if (!String(p).startsWith("auto")) return null;
2883
- const alignment = p === "auto-start" ? "start" : p === "auto-end" ? "end" : void 0;
2884
- return autoPlacement({ alignment });
2885
- }
2886
- function toOffsetMw(opt) {
2887
- if (opt == null) return null;
2888
- if (Array.isArray(opt)) {
2889
- const [crossAxis, mainAxis] = opt;
2890
- return offset({ crossAxis, mainAxis });
2891
- }
2892
- if (typeof opt === "number") return offset(opt);
2893
- return offset(opt);
2894
- }
2895
- function usePopoverPosition({
2896
- allowFlip = true,
2897
- allowShift = true,
2898
- autoUpdateOptions,
2899
- fitAvailableSpace = false,
2900
- freeze = false,
2901
- offset: offset2,
2902
- placement = "bottom-start",
2903
- shiftOptions
2904
- }) {
2905
- const autoMw = autoMiddlewareFor(placement);
2906
- const offsetMiddleware = toOffsetMw(offset2);
2907
- const isSidePlacement = placement.startsWith("left") || placement.startsWith("right");
2908
- const mergedShiftOptions = shiftOptions ? { padding: 8, ...shiftOptions } : { padding: 8 };
2909
- const middleware = [
2910
- // offset first (mirrors common Popper setups)
2911
- ...offsetMiddleware ? [offsetMiddleware] : [],
2912
- // choose between autoPlacement (Popper's "auto*") OR flip()
2913
- // only allow flip when not explicitly 'left*' or 'right*'
2914
- ...autoMw ? [autoMw] : allowFlip && !isSidePlacement ? [flip()] : [],
2915
- // viewport collision adjustments
2916
- ...allowShift ? [shift(mergedShiftOptions)] : [],
2917
- // optional size constraining
2918
- // eslint-disable-next-line @typescript-eslint/no-empty-function
2919
- ...fitAvailableSpace ? [size({ apply: () => {
2920
- } })] : []
2921
- ];
2922
- const seedPlacement = String(placement).startsWith("auto") ? "bottom" : placement;
2923
- return useFloating({
2924
- middleware,
2925
- placement: seedPlacement,
2926
- strategy: "fixed",
2927
- whileElementsMounted: freeze ? void 0 : (reference, floating, update) => autoUpdate(reference, floating, update, {
2928
- ancestorResize: true,
2929
- ancestorScroll: true,
2930
- animationFrame: false,
2931
- elementResize: hasResizeObserver,
2932
- ...autoUpdateOptions
2933
- })
2934
- });
2935
- }
2936
- const LegacyThreadContext = React.createContext({ legacyThread: void 0 });
2937
- const useLegacyThreadContext = () => useContext(LegacyThreadContext);
2938
3009
  const cooldownTimerStateSelector = (state) => ({
2939
3010
  isCooldownActive: !!state.cooldownRemaining
2940
3011
  });
@@ -4182,7 +4253,31 @@ const UnreadCountBadge = ({
4182
4253
  }
4183
4254
  )
4184
4255
  ] });
4256
+ const DEFAULT_CHAT_VIEW_A11Y_CONTEXT_VALUE = {
4257
+ chatViewPanelIds: {
4258
+ channels: "str-chat__chat-view-panel-channels",
4259
+ threads: "str-chat__chat-view-panel-threads"
4260
+ },
4261
+ chatViewTabIds: {
4262
+ channels: "str-chat__chat-view-tab-channels",
4263
+ threads: "str-chat__chat-view-tab-threads"
4264
+ }
4265
+ };
4266
+ const createChatViewA11yContextValue = (chatViewId) => ({
4267
+ // Keep IDs unique per ChatView instance so ARIA references do not collide.
4268
+ chatViewPanelIds: {
4269
+ channels: `str-chat__chat-view-${chatViewId}-panel-channels`,
4270
+ threads: `str-chat__chat-view-${chatViewId}-panel-threads`
4271
+ },
4272
+ chatViewTabIds: {
4273
+ channels: `str-chat__chat-view-${chatViewId}-tab-channels`,
4274
+ threads: `str-chat__chat-view-${chatViewId}-tab-threads`
4275
+ }
4276
+ });
4185
4277
  const ChatViewContext = createContext(void 0);
4278
+ const ChatViewA11yContext = createContext(
4279
+ DEFAULT_CHAT_VIEW_A11Y_CONTEXT_VALUE
4280
+ );
4186
4281
  const useChatViewContext = () => {
4187
4282
  const value = useContext(ChatViewContext);
4188
4283
  if (!value) {
@@ -4193,16 +4288,33 @@ const useChatViewContext = () => {
4193
4288
  }
4194
4289
  return value;
4195
4290
  };
4291
+ const useChatViewA11yContext = () => useContext(ChatViewA11yContext);
4196
4292
  const ChatView = ({ children }) => {
4197
4293
  const [activeChatView, setActiveChatView] = useState("channels");
4294
+ const chatViewId = useId().replace(/:/g, "");
4198
4295
  const { theme } = useChatContext();
4296
+ const a11yValue = useMemo(
4297
+ () => createChatViewA11yContextValue(chatViewId),
4298
+ [chatViewId]
4299
+ );
4199
4300
  const value = useMemo(() => ({ activeChatView, setActiveChatView }), [activeChatView]);
4200
- return /* @__PURE__ */ jsx(ChatViewContext.Provider, { value, children: /* @__PURE__ */ jsx("div", { className: clsx("str-chat", theme, "str-chat__chat-view"), children }) });
4301
+ return /* @__PURE__ */ jsx(ChatViewA11yContext.Provider, { value: a11yValue, children: /* @__PURE__ */ jsx(ChatViewContext.Provider, { value, children: /* @__PURE__ */ jsx("div", { className: clsx("str-chat", theme, "str-chat__chat-view"), children }) }) });
4201
4302
  };
4202
4303
  const ChannelsView = ({ children }) => {
4203
4304
  const { activeChatView } = useChatViewContext();
4204
- if (activeChatView !== "channels") return null;
4205
- return /* @__PURE__ */ jsx("div", { className: "str-chat__chat-view__channels", children });
4305
+ const { chatViewPanelIds, chatViewTabIds } = useChatViewA11yContext();
4306
+ const isActive = activeChatView === "channels";
4307
+ if (!isActive) return null;
4308
+ return /* @__PURE__ */ jsx(
4309
+ "div",
4310
+ {
4311
+ "aria-labelledby": chatViewTabIds.channels,
4312
+ className: "str-chat__chat-view__channels",
4313
+ id: chatViewPanelIds.channels,
4314
+ role: "tabpanel",
4315
+ children
4316
+ }
4317
+ );
4206
4318
  };
4207
4319
  const ThreadsViewContext = createContext({
4208
4320
  activeThread: void 0,
@@ -4211,10 +4323,21 @@ const ThreadsViewContext = createContext({
4211
4323
  const useThreadsViewContext = () => useContext(ThreadsViewContext);
4212
4324
  const ThreadsView = ({ children }) => {
4213
4325
  const { activeChatView } = useChatViewContext();
4326
+ const { chatViewPanelIds, chatViewTabIds } = useChatViewA11yContext();
4214
4327
  const [activeThread, setActiveThread] = useState(void 0);
4215
4328
  const value = useMemo(() => ({ activeThread, setActiveThread }), [activeThread]);
4216
- if (activeChatView !== "threads") return null;
4217
- return /* @__PURE__ */ jsx(ThreadsViewContext.Provider, { value, children: /* @__PURE__ */ jsx("div", { className: "str-chat__chat-view__threads", children }) });
4329
+ const isActive = activeChatView === "threads";
4330
+ if (!isActive) return null;
4331
+ return /* @__PURE__ */ jsx(ThreadsViewContext.Provider, { value, children: /* @__PURE__ */ jsx(
4332
+ "div",
4333
+ {
4334
+ "aria-labelledby": chatViewTabIds.threads,
4335
+ className: "str-chat__chat-view__threads",
4336
+ id: chatViewPanelIds.threads,
4337
+ role: "tabpanel",
4338
+ children
4339
+ }
4340
+ ) });
4218
4341
  };
4219
4342
  const useActiveThread = ({ activeThread }) => {
4220
4343
  useEffect(() => {
@@ -4310,17 +4433,22 @@ const ChatViewChannelsSelectorButton = ({
4310
4433
  iconOnly = true
4311
4434
  }) => {
4312
4435
  const { activeChatView, setActiveChatView } = useChatViewContext();
4436
+ const { chatViewPanelIds, chatViewTabIds } = useChatViewA11yContext();
4313
4437
  const { t } = useTranslationContext();
4314
4438
  const isActive = activeChatView === "channels";
4315
4439
  return /* @__PURE__ */ jsx(
4316
4440
  ChatViewSelectorButton,
4317
4441
  {
4318
4442
  ActiveIcon: IconMessageBubbleFill,
4443
+ "aria-controls": chatViewPanelIds.channels,
4319
4444
  "aria-selected": isActive,
4320
4445
  Icon: IconMessageBubble,
4321
4446
  iconOnly,
4447
+ id: chatViewTabIds.channels,
4322
4448
  isActive,
4449
+ onClick: () => setActiveChatView("channels"),
4323
4450
  onPointerDown: () => setActiveChatView("channels"),
4451
+ tabIndex: 0,
4324
4452
  text: t("Channels")
4325
4453
  }
4326
4454
  );
@@ -4336,17 +4464,22 @@ const ChatViewThreadsSelectorButton = ({
4336
4464
  unreadThreadCount: 0
4337
4465
  };
4338
4466
  const { activeChatView, setActiveChatView } = useChatViewContext();
4467
+ const { chatViewPanelIds, chatViewTabIds } = useChatViewA11yContext();
4339
4468
  const { t } = useTranslationContext();
4340
4469
  const isActive = activeChatView === "threads";
4341
4470
  return /* @__PURE__ */ jsx(
4342
4471
  ChatViewSelectorButton,
4343
4472
  {
4344
4473
  ActiveIcon: IconThreadFill,
4474
+ "aria-controls": chatViewPanelIds.threads,
4345
4475
  "aria-selected": isActive,
4346
4476
  Icon: IconThread,
4347
4477
  iconOnly,
4478
+ id: chatViewTabIds.threads,
4348
4479
  isActive,
4480
+ onClick: () => setActiveChatView("threads"),
4349
4481
  onPointerDown: () => setActiveChatView("threads"),
4482
+ tabIndex: 0,
4350
4483
  text: t("Threads"),
4351
4484
  children: /* @__PURE__ */ jsx(UnreadCountBadge, { count: unreadThreadCount, position: "top-right", children: isActive ? /* @__PURE__ */ jsx(IconThreadFill, {}) : /* @__PURE__ */ jsx(IconThread, {}) })
4352
4485
  }
@@ -4365,7 +4498,18 @@ const defaultChatViewSelectorItemSet = [
4365
4498
  const ChatViewSelector = ({
4366
4499
  iconOnly = true,
4367
4500
  itemSet = defaultChatViewSelectorItemSet
4368
- }) => /* @__PURE__ */ jsx("div", { className: "str-chat__chat-view__selector", children: itemSet.map(({ Component, type }) => /* @__PURE__ */ jsx(Component, { iconOnly }, type)) });
4501
+ }) => {
4502
+ const { t } = useTranslationContext();
4503
+ return /* @__PURE__ */ jsx(
4504
+ "div",
4505
+ {
4506
+ "aria-label": t("aria/Chat view tabs"),
4507
+ className: "str-chat__chat-view__selector",
4508
+ role: "tablist",
4509
+ children: itemSet.map(({ Component, type }) => /* @__PURE__ */ jsx(Component, { iconOnly }, type))
4510
+ }
4511
+ );
4512
+ };
4369
4513
  ChatView.Channels = ChannelsView;
4370
4514
  ChatView.Threads = ThreadsView;
4371
4515
  ChatView.ThreadAdapter = ThreadAdapter;
@@ -4491,177 +4635,112 @@ const useNotificationApi = () => {
4491
4635
  startNotificationTimeout
4492
4636
  };
4493
4637
  };
4494
- const AudioPlayerContext = React.createContext({
4495
- audioPlayers: null
4496
- });
4497
- const WithAudioPlayback = ({
4498
- allowConcurrentPlayback,
4499
- children
4500
- }) => {
4501
- const [audioPlayers] = useState(() => new AudioPlayerPool({ allowConcurrentPlayback }));
4502
- useEffect(
4503
- () => () => {
4504
- audioPlayers.clear();
4505
- },
4506
- [audioPlayers]
4507
- );
4508
- return /* @__PURE__ */ jsx(AudioPlayerContext.Provider, { value: { audioPlayers }, children });
4509
- };
4510
- const makeAudioPlayerId = ({ requester, src }) => `${requester ?? "requester-unknown"}:${src}`;
4511
- const useAudioPlayer = ({
4512
- durationSeconds,
4513
- fileSize,
4514
- mimeType,
4515
- playbackRates,
4516
- plugins,
4517
- requester = "",
4518
- src,
4519
- title,
4520
- waveformData
4521
- }) => {
4522
- const { addNotification } = useNotificationApi();
4523
- const panel = useNotificationTarget();
4524
- const { t } = useTranslationContext();
4525
- const { audioPlayers } = useContext(AudioPlayerContext);
4526
- const audioPlayer = src && audioPlayers ? audioPlayers.getOrAdd({
4527
- durationSeconds,
4528
- fileSize,
4529
- id: makeAudioPlayerId({ requester, src }),
4530
- mimeType,
4531
- playbackRates,
4532
- plugins,
4533
- src,
4534
- title,
4535
- waveformData
4536
- }) : void 0;
4537
- useEffect(() => {
4538
- if (!audioPlayer) return;
4539
- const notificationsPlugin = audioPlayerNotificationsPluginFactory({
4540
- addNotification,
4541
- panel,
4542
- t
4543
- });
4544
- audioPlayer.setPlugins((currentPlugins) => [
4545
- ...currentPlugins.filter((plugin) => plugin.id !== notificationsPlugin.id),
4546
- notificationsPlugin
4547
- ]);
4548
- }, [addNotification, audioPlayer, panel, t]);
4549
- return audioPlayer;
4550
- };
4551
- const activeAudioPlayerSelector = ({ activeAudioPlayer }) => ({
4552
- activeAudioPlayer
4553
- });
4554
- const useActiveAudioPlayer = () => {
4555
- const { audioPlayers } = useContext(AudioPlayerContext);
4556
- const { activeAudioPlayer } = useStateStore(audioPlayers?.state, activeAudioPlayerSelector) ?? {};
4557
- return activeAudioPlayer;
4558
- };
4559
4638
  export {
4560
- IconLocation as $,
4561
- IconExclamationMark as A,
4639
+ IconGiphy as $,
4640
+ IconNoSign as A,
4562
4641
  Button as B,
4563
4642
  ComponentContext as C,
4564
- IconNoSign as D,
4565
- isMessageDeleted as E,
4566
- isMessageErrorRetryable as F,
4567
- ACTIONS_NOT_WORKING_IN_THREAD as G,
4568
- useNotificationApi as H,
4569
- IconDownload as I,
4570
- IconArrowUpRight as J,
4571
- IconPin as K,
4643
+ isMessageDeleted as D,
4644
+ isMessageErrorRetryable as E,
4645
+ ACTIONS_NOT_WORKING_IN_THREAD as F,
4646
+ useNotificationApi as G,
4647
+ IconArrowUpRight as H,
4648
+ IconPauseFill as I,
4649
+ IconPin as J,
4650
+ mapToUserNameOrId as K,
4572
4651
  LocalizedFormat as L,
4573
- mapToUserNameOrId as M,
4574
- IconClock as N,
4575
- IconCheckmark1Small as O,
4576
- IconChecks as P,
4577
- getReadByTooltipText as Q,
4578
- messageHasAttachments as R,
4579
- messageTextHasEmojisOnly as S,
4580
- isDate as T,
4581
- getDateString as U,
4582
- IconTranslate as V,
4583
- useMessageComposerContext as W,
4584
- useIsCooldownActive as X,
4585
- IconXmarkSmall as Y,
4586
- IconImage as Z,
4587
- IconPoll as _,
4652
+ IconClock as M,
4653
+ IconCheckmark1Small as N,
4654
+ IconChecks as O,
4655
+ getReadByTooltipText as P,
4656
+ messageHasAttachments as Q,
4657
+ messageTextHasEmojisOnly as R,
4658
+ isDate as S,
4659
+ getDateString as T,
4660
+ IconTranslate as U,
4661
+ IconDownload as V,
4662
+ useAudioPlayer as W,
4663
+ IconImage as X,
4664
+ IconArrowDownCircle as Y,
4665
+ IconRetry as Z,
4666
+ IconLink as _,
4588
4667
  useMessageComposerController as a,
4589
- areMessagePropsEqual as a$,
4590
- IconFile as a0,
4591
- IconLink as a1,
4592
- IconVideo as a2,
4593
- IconCamera as a3,
4594
- IconVoice as a4,
4595
- IconBookmark as a5,
4596
- IconBell as a6,
4597
- IconChevronDown as a7,
4598
- IconMinus as a8,
4599
- IconPlusSmall as a9,
4600
- MessageComposerContextProvider as aA,
4601
- useTypingContext as aB,
4602
- useChatViewContext as aC,
4603
- MESSAGE_ACTIONS as aD,
4604
- LegacyThreadContext as aE,
4605
- IconEmojiAdd as aF,
4606
- IconReply as aG,
4607
- IconEmoji as aH,
4608
- IconMore as aI,
4609
- IconUserCheck as aJ,
4610
- IconBookmarkRemove as aK,
4611
- IconBellOff as aL,
4612
- IconNotification as aM,
4613
- IconEdit as aN,
4614
- IconCopy as aO,
4615
- IconUnpin as aP,
4616
- IconQuote as aQ,
4617
- IconThread as aR,
4618
- areMessageUIPropsEqual as aS,
4619
- isDateSeparatorMessage as aT,
4620
- isMessageBlocked as aU,
4621
- messageHasSingleAttachment as aV,
4622
- messageHasGiphyAttachment as aW,
4623
- messageHasReactions as aX,
4624
- messageHasQuotedMessage as aY,
4625
- isMessageEdited as aZ,
4626
- countEmojis as a_,
4627
- IconCheckmark as aa,
4628
- DEFAULT_LOAD_PAGE_SCROLL_THRESHOLD as ab,
4629
- IconTrophy as ac,
4630
- IconReorder as ad,
4631
- IconMinusCircle as ae,
4632
- IconSend as af,
4633
- IconAudio as ag,
4634
- IconUserAdd as ah,
4635
- IconMute as ai,
4636
- IconGiphy as aj,
4637
- IconFlag as ak,
4638
- IconUserRemove as al,
4639
- IconAttachment as am,
4640
- IconCommand as an,
4641
- CHANNEL_CONTAINER_ID as ao,
4642
- IconPlus as ap,
4643
- IconUnsupportedAttachment as aq,
4644
- IconExclamationTriangleFill as ar,
4645
- useAudioPlayer as as,
4646
- IconMicrophoneSolid as at,
4647
- IconVideoFill as au,
4648
- IconRetry as av,
4649
- IconArrowDownCircle as aw,
4650
- IconBolt as ax,
4651
- IconDelete as ay,
4652
- IconUpload as az,
4668
+ countEmojis as a$,
4669
+ IconLocation as a0,
4670
+ IconUnsupportedAttachment as a1,
4671
+ IconEyeFill as a2,
4672
+ useMessageComposerContext as a3,
4673
+ useIsCooldownActive as a4,
4674
+ IconXmarkSmall as a5,
4675
+ IconPoll as a6,
4676
+ IconFile as a7,
4677
+ IconVideo as a8,
4678
+ IconCamera as a9,
4679
+ IconUpload as aA,
4680
+ MessageComposerContextProvider as aB,
4681
+ useTypingContext as aC,
4682
+ useChatViewContext as aD,
4683
+ MESSAGE_ACTIONS as aE,
4684
+ LegacyThreadContext as aF,
4685
+ IconEmojiAdd as aG,
4686
+ IconReply as aH,
4687
+ IconEmoji as aI,
4688
+ IconMore as aJ,
4689
+ IconUserCheck as aK,
4690
+ IconBookmarkRemove as aL,
4691
+ IconBellOff as aM,
4692
+ IconNotification as aN,
4693
+ IconEdit as aO,
4694
+ IconCopy as aP,
4695
+ IconUnpin as aQ,
4696
+ IconQuote as aR,
4697
+ IconThread as aS,
4698
+ areMessageUIPropsEqual as aT,
4699
+ isDateSeparatorMessage as aU,
4700
+ isMessageBlocked as aV,
4701
+ messageHasSingleAttachment as aW,
4702
+ messageHasGiphyAttachment as aX,
4703
+ messageHasReactions as aY,
4704
+ messageHasQuotedMessage as aZ,
4705
+ isMessageEdited as a_,
4706
+ IconVoice as aa,
4707
+ IconBookmark as ab,
4708
+ IconBell as ac,
4709
+ IconChevronDown as ad,
4710
+ IconMinus as ae,
4711
+ IconPlusSmall as af,
4712
+ IconCheckmark as ag,
4713
+ DEFAULT_LOAD_PAGE_SCROLL_THRESHOLD as ah,
4714
+ IconTrophy as ai,
4715
+ IconReorder as aj,
4716
+ IconMinusCircle as ak,
4717
+ IconSend as al,
4718
+ IconAudio as am,
4719
+ IconUserAdd as an,
4720
+ IconMute as ao,
4721
+ IconFlag as ap,
4722
+ IconUserRemove as aq,
4723
+ IconAttachment as ar,
4724
+ IconCommand as as,
4725
+ CHANNEL_CONTAINER_ID as at,
4726
+ IconPlus as au,
4727
+ IconExclamationTriangleFill as av,
4728
+ IconMicrophoneSolid as aw,
4729
+ IconVideoFill as ax,
4730
+ IconBolt as ay,
4731
+ IconDelete as az,
4653
4732
  useChannelActionContext as b,
4654
4733
  getNotificationTargetTag as b$,
4655
- getMessageActions as b0,
4656
- processMessages as b1,
4657
- insertIntro as b2,
4658
- getGroupStyles as b3,
4659
- getLastReceived as b4,
4660
- IconArrowUp as b5,
4661
- isIntroMessage as b6,
4662
- isLocalMessage as b7,
4663
- getIsFirstUnreadMessage as b8,
4664
- IconArrowDown as b9,
4734
+ areMessagePropsEqual as b0,
4735
+ getMessageActions as b1,
4736
+ processMessages as b2,
4737
+ insertIntro as b3,
4738
+ getGroupStyles as b4,
4739
+ getLastReceived as b5,
4740
+ IconArrowUp as b6,
4741
+ isIntroMessage as b7,
4742
+ isLocalMessage as b8,
4743
+ getIsFirstUnreadMessage as b9,
4665
4744
  useEditMessageHandler as bA,
4666
4745
  useMentionsHandlers as bB,
4667
4746
  Channel as bC,
@@ -4689,23 +4768,23 @@ export {
4689
4768
  isNotificationTargetPanel as bY,
4690
4769
  getNotificationTargetPanel as bZ,
4691
4770
  getNotificationTargetPanels as b_,
4692
- DEFAULT_NEXT_CHANNEL_PAGE_SIZE as ba,
4693
- EmptyStateIndicator as bb,
4694
- useNotificationTarget as bc,
4695
- getChannel as bd,
4696
- IconSearch as be,
4697
- IconXCircle as bf,
4698
- useChannelListContext as bg,
4699
- DEFAULT_JUMP_TO_PAGE_SIZE as bh,
4700
- ChannelListContextProvider as bi,
4701
- IconLeave as bj,
4702
- IconArchive as bk,
4703
- IconExclamationCircleFill as bl,
4704
- useThreadsViewContext as bm,
4705
- IconMessageBubbles as bn,
4706
- IconRefresh as bo,
4707
- hasSystemNotificationTag as bp,
4708
- IconEyeFill as bq,
4771
+ IconArrowDown as ba,
4772
+ DEFAULT_NEXT_CHANNEL_PAGE_SIZE as bb,
4773
+ EmptyStateIndicator as bc,
4774
+ useNotificationTarget as bd,
4775
+ getChannel as be,
4776
+ IconSearch as bf,
4777
+ IconXCircle as bg,
4778
+ useChannelListContext as bh,
4779
+ DEFAULT_JUMP_TO_PAGE_SIZE as bi,
4780
+ ChannelListContextProvider as bj,
4781
+ IconLeave as bk,
4782
+ IconArchive as bl,
4783
+ IconExclamationCircleFill as bm,
4784
+ useThreadsViewContext as bn,
4785
+ IconMessageBubbles as bo,
4786
+ IconRefresh as bp,
4787
+ hasSystemNotificationTag as bq,
4709
4788
  defaultDateTimeParser as br,
4710
4789
  isLanguageSupported as bs,
4711
4790
  ChatProvider as bt,
@@ -4736,25 +4815,25 @@ export {
4736
4815
  useChannelStateContext as d,
4737
4816
  useChatContext as e,
4738
4817
  isNotificationForPanel as f,
4739
- IconPauseFill as g,
4740
- IconPlayFill as h,
4818
+ IconPlayFill as g,
4819
+ getDefaultExportFromCjs as h,
4741
4820
  isMessageBounced as i,
4742
- getDefaultExportFromCjs as j,
4743
- defaultTranslatorFunction as k,
4744
- calendar as l,
4745
- IconLoading as m,
4746
- useComponentContext as n,
4747
- isNetworkSendFailure as o,
4821
+ defaultTranslatorFunction as j,
4822
+ calendar as k,
4823
+ IconLoading as l,
4824
+ useComponentContext as m,
4825
+ isNetworkSendFailure as n,
4826
+ isUserMuted as o,
4748
4827
  predefinedFormatters as p,
4749
- isUserMuted as q,
4750
- useThreadContext as r,
4751
- usePopoverPosition as s,
4752
- IconXmark as t,
4828
+ useThreadContext as q,
4829
+ usePopoverPosition as r,
4830
+ IconXmark as s,
4831
+ IconUser as t,
4753
4832
  useStateStore as u,
4754
- IconUser as v,
4755
- IconExclamationMarkFill as w,
4756
- IconChevronRight as x,
4757
- IconChevronLeft as y,
4758
- IconArrowLeft as z
4833
+ IconExclamationMarkFill as v,
4834
+ IconChevronRight as w,
4835
+ IconChevronLeft as x,
4836
+ IconArrowLeft as y,
4837
+ IconExclamationMark as z
4759
4838
  };
4760
- //# sourceMappingURL=WithAudioPlayback.9b779236.mjs.map
4839
+ //# sourceMappingURL=useNotificationApi.e0c52de6.mjs.map