stream-chat-react 14.2.0 → 14.3.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.
package/README.md CHANGED
@@ -54,26 +54,26 @@ For complete pricing and details visit our [Chat Pricing Page](https://getstream
54
54
 
55
55
  ## Example Applications
56
56
 
57
- We have built five demo applications showcasing a variety of chat use cases, including social messaging, team collaboration, customer support, livestream gaming, and virtual event. You can preview these [demos](https://getstream.io/chat/demos/) on our website. Also, the code is [open source](https://github.com/GetStream/website-react-examples/).
57
+ We have built demo applications showcasing a variety of chat use cases, including social messaging, team collaboration, customer support, livestream gaming, and virtual events. You can preview these [demos](https://getstream.io/chat/demos/) on our website.
58
58
 
59
59
  ## Documentation
60
60
 
61
61
  We use a doc generator to build our [component documentation](https://getstream.io/chat/docs/sdk/react/). We provide a brief description of each chat component and define all of the props it accepts.
62
62
 
63
- The React components are created using the [stream-chat](https://github.com/getstream/stream-chat-js) library. If you're customizing the components, it's likely you'll need to make additional calls to our Chat API using our JavaScript client, which has [documentation](https://getstream.io/chat/docs/javascript/) on our website.
63
+ The React components are created using the [stream-chat](https://github.com/getstream/stream-chat-js) library. If you're customizing the components, you'll likely need to make additional calls to our Chat API using our JavaScript client, which has [documentation](https://getstream.io/chat/docs/javascript/) on our website.
64
64
 
65
65
  ## Component Reusability
66
66
 
67
- For components that implement significant logic, it's helpful to split the component into two parts: a top-level component which handles functionality and a lower level component which renders the UI. This way you can swap UI without altering the logic that gives the component its functionality. We use this provider/consumer pattern frequently in the library, and the below example shows how to swap out the `Message` UI component with `CustomMessageUI` (using `WithComponents`), without affecting any logic in the application.
67
+ For components that implement significant logic, it's helpful to split the component into two parts: a top-level component that handles functionality and a lower-level component that renders the UI. This way, you can swap UI without altering the logic that gives the component its functionality. We use this provider/consumer pattern frequently in the library, and the below example shows how to swap out the `Message` UI component with `CustomMessageUI` (using `WithComponents`), without affecting any logic in the application.
68
68
 
69
69
  ```jsx
70
70
  <Channel>
71
71
  <Window>
72
72
  <ChannelHeader />
73
- <WithComponents overrides={{ Message: CustomMessageUI }}>
73
+ <WithComponents overrides={{ MessageUI: CustomMessageUI }}>
74
74
  <MessageList />
75
75
  </WithComponents>
76
- <MessageInput />
76
+ <MessageComposer />
77
77
  </Window>
78
78
  <Thread />
79
79
  </Channel>
@@ -81,14 +81,12 @@ For components that implement significant logic, it's helpful to split the compo
81
81
 
82
82
  ### Customizing Styles
83
83
 
84
- The preferred method for overriding the pre-defined styles in the library is to two-step process. First, import our bundled CSS into your main CSS file (or CSS file loaded with your chat application). Second, locate any Stream styles you want to override using either the browser inspector or by viewing the library code. You can then add selectors to your local CSS file to override our defaults (ideally within the stream-overrides layer). Layers (when ordered correctly, see example) ensure that your overrides take precedence even if your overriding selectors are less specific. For example:
84
+ The preferred method for overriding the pre-defined styles in the library is a two-step process. First, import our bundled CSS into your main CSS file (or CSS file loaded with your chat application). Second, locate any Stream styles you want to override using either the browser inspector or by viewing the library code. You can then add selectors to your local CSS file to override our defaults (ideally within the stream-overrides layer). Layers (when ordered correctly, see example) ensure that your overrides take precedence even if your overriding selectors are less specific. For example:
85
85
 
86
86
  ```css title="index.css"
87
87
  @layer stream, stream-overrides;
88
88
 
89
- @import 'stream-chat-react/css/v2/index.css' layer(stream);
90
- /* or */
91
- @import 'stream-chat-react/dist/css/v2/index.css' layer(stream);
89
+ @import 'stream-chat-react/css/index.css' layer(stream);
92
90
 
93
91
  @layer stream-overrides {
94
92
  /* your overrides */
package/dist/cjs/index.js CHANGED
@@ -22380,6 +22380,7 @@ const defaultReactionOptions = {
22380
22380
  }
22381
22381
  }
22382
22382
  };
22383
+ const getHasExtendedReactions = (reactionOptions) => !Array.isArray(reactionOptions) && typeof reactionOptions.extended !== "undefined" && Object.keys(reactionOptions.extended).length > 0;
22383
22384
  const stableOwnReactions = [];
22384
22385
  const ReactionSelector = (props) => {
22385
22386
  const {
@@ -22425,7 +22426,7 @@ const ReactionSelector = (props) => {
22425
22426
  })
22426
22427
  );
22427
22428
  }, [reactionOptions]);
22428
- const hasExtendedReactions = !Array.isArray(reactionOptions) && reactionOptions.extended && Object.keys(reactionOptions.extended).length > 0;
22429
+ const hasExtendedReactions = getHasExtendedReactions(reactionOptions);
22429
22430
  return /* @__PURE__ */ jsxRuntime.jsx(
22430
22431
  "div",
22431
22432
  {
@@ -22656,6 +22657,7 @@ const MessageReactionsDetail = ({
22656
22657
  reactionDetailsSort: contextReactionDetailsSort
22657
22658
  } = useMessageContext(MessageReactionsDetail.name);
22658
22659
  const reactionDetailsSort = propReactionDetailsSort ?? contextReactionDetailsSort ?? defaultReactionDetailsSort;
22660
+ const hasExtendedReactions = getHasExtendedReactions(reactionOptions);
22659
22661
  const {
22660
22662
  isLoading: areReactionsLoading,
22661
22663
  reactions: reactionDetails,
@@ -22702,7 +22704,7 @@ const MessageReactionsDetail = ({
22702
22704
  className: "str-chat__message-reactions-detail__reaction-type-list",
22703
22705
  "data-testid": "reaction-type-list",
22704
22706
  children: [
22705
- /* @__PURE__ */ jsxRuntime.jsx("li", { className: "str-chat__message-reactions-detail__reaction-type-list-item", children: /* @__PURE__ */ jsxRuntime.jsx(
22707
+ hasExtendedReactions && /* @__PURE__ */ jsxRuntime.jsx("li", { className: "str-chat__message-reactions-detail__reaction-type-list-item", children: /* @__PURE__ */ jsxRuntime.jsx(
22706
22708
  "button",
22707
22709
  {
22708
22710
  "aria-label": t("Add reaction"),
@@ -27292,6 +27294,9 @@ const useConnectionRecoveredListener = (forceUpdate) => {
27292
27294
  };
27293
27295
  const RECOVER_LOADED_CHANNELS_THROTTLE_INTERVAL_IN_MS = 5e3;
27294
27296
  const MIN_RECOVER_LOADED_CHANNELS_THROTTLE_INTERVAL_IN_MS = 2e3;
27297
+ const mapPredefinedFilterSortToChannelSort = (sort) => sort.map(({ direction = 1, field }) => ({
27298
+ [field]: direction
27299
+ }));
27295
27300
  const usePaginatedChannels = (client, filters, sort, options, activeChannelHandler, recoveryThrottleIntervalMs = RECOVER_LOADED_CHANNELS_THROTTLE_INTERVAL_IN_MS, customQueryChannels) => {
27296
27301
  const { addNotification } = useNotificationApi.useNotificationApi();
27297
27302
  const {
@@ -27300,6 +27305,10 @@ const usePaginatedChannels = (client, filters, sort, options, activeChannelHandl
27300
27305
  const { t } = useNotificationApi.useTranslationContext();
27301
27306
  const [channels, setChannels] = React.useState([]);
27302
27307
  const [hasNextPage, setHasNextPage] = React.useState(true);
27308
+ const [responseFilters, setResponseFilters] = React.useState(
27309
+ void 0
27310
+ );
27311
+ const [responseSort, setResponseSort] = React.useState(void 0);
27303
27312
  const lastRecoveryTimestamp = React.useRef(void 0);
27304
27313
  const recoveryThrottleInterval = recoveryThrottleIntervalMs < MIN_RECOVER_LOADED_CHANNELS_THROTTLE_INTERVAL_IN_MS ? MIN_RECOVER_LOADED_CHANNELS_THROTTLE_INTERVAL_IN_MS : recoveryThrottleIntervalMs ?? RECOVER_LOADED_CHANNELS_THROTTLE_INTERVAL_IN_MS;
27305
27314
  const filterString = React.useMemo(() => JSON.stringify(filters), [filters]);
@@ -27320,6 +27329,8 @@ const usePaginatedChannels = (client, filters, sort, options, activeChannelHandl
27320
27329
  setChannels,
27321
27330
  setHasNextPage
27322
27331
  });
27332
+ setResponseFilters(void 0);
27333
+ setResponseSort(void 0);
27323
27334
  } else {
27324
27335
  const newOptions = {
27325
27336
  offset,
@@ -27328,13 +27339,22 @@ const usePaginatedChannels = (client, filters, sort, options, activeChannelHandl
27328
27339
  const channelQueryResponse = await client.queryChannels(
27329
27340
  filters,
27330
27341
  sort || {},
27331
- newOptions
27342
+ newOptions,
27343
+ { withResponse: true }
27332
27344
  );
27333
- const newChannels = queryType === "reload" ? channelQueryResponse : uniqBy([...channels, ...channelQueryResponse], "cid");
27345
+ const newChannels = queryType === "reload" ? channelQueryResponse.channels : uniqBy([...channels, ...channelQueryResponse.channels], "cid");
27334
27346
  setChannels(newChannels);
27335
- setHasNextPage(channelQueryResponse.length >= (newOptions.limit ?? 1));
27347
+ setHasNextPage(channelQueryResponse.channels.length >= (newOptions.limit ?? 1));
27348
+ const predefinedFilter = channelQueryResponse.predefined_filter;
27349
+ const nextResponseFilters = predefinedFilter ? predefinedFilter.filter : void 0;
27350
+ const nextResponseSort = predefinedFilter?.sort ? mapPredefinedFilterSortToChannelSort(predefinedFilter.sort) : void 0;
27351
+ setResponseFilters(nextResponseFilters);
27352
+ setResponseSort(nextResponseSort);
27336
27353
  if (!offset && activeChannelHandler) {
27337
- activeChannelHandler(newChannels, setChannels);
27354
+ activeChannelHandler(newChannels, setChannels, {
27355
+ filters: nextResponseFilters ?? filters,
27356
+ sort: nextResponseSort ?? sort
27357
+ });
27338
27358
  }
27339
27359
  }
27340
27360
  } catch (error2) {
@@ -27374,8 +27394,12 @@ const usePaginatedChannels = (client, filters, sort, options, activeChannelHandl
27374
27394
  React.useEffect(() => {
27375
27395
  queryChannels("reload");
27376
27396
  }, [filterString, sortString]);
27397
+ const effectiveFilters = responseFilters ?? filters;
27398
+ const effectiveSort = responseSort ?? sort;
27377
27399
  return {
27378
27400
  channels,
27401
+ effectiveFilters,
27402
+ effectiveSort,
27379
27403
  hasNextPage,
27380
27404
  loadNextPage,
27381
27405
  setChannels
@@ -28521,7 +28545,7 @@ const UnMemoizedChannelList = (props) => {
28521
28545
  searchController.state,
28522
28546
  searchControllerStateSelector
28523
28547
  );
28524
- const activeChannelHandler = async (channels2, setChannels2) => {
28548
+ const activeChannelHandler = async (channels2, setChannels2, effectiveQueryParams) => {
28525
28549
  if (!channels2.length) {
28526
28550
  return;
28527
28551
  }
@@ -28539,7 +28563,7 @@ const UnMemoizedChannelList = (props) => {
28539
28563
  const newChannels = moveChannelUpwards({
28540
28564
  channels: channels2,
28541
28565
  channelToMove: customActiveChannelObject,
28542
- sort
28566
+ sort: effectiveQueryParams.sort
28543
28567
  });
28544
28568
  setChannels2(newChannels);
28545
28569
  return;
@@ -28550,7 +28574,14 @@ const UnMemoizedChannelList = (props) => {
28550
28574
  }
28551
28575
  };
28552
28576
  const forceUpdate = React.useCallback(() => setChannelUpdateCount((count) => count + 1), []);
28553
- const { channels, hasNextPage, loadNextPage, setChannels } = usePaginatedChannels(
28577
+ const {
28578
+ channels,
28579
+ effectiveFilters,
28580
+ effectiveSort,
28581
+ hasNextPage,
28582
+ loadNextPage,
28583
+ setChannels
28584
+ } = usePaginatedChannels(
28554
28585
  client,
28555
28586
  filters || DEFAULT_FILTERS,
28556
28587
  sort || DEFAULT_SORT,
@@ -28562,7 +28593,11 @@ const UnMemoizedChannelList = (props) => {
28562
28593
  const loadedChannels = channelRenderFilterFn ? channelRenderFilterFn(channels) : channels;
28563
28594
  const { customHandler, defaultHandler } = usePrepareShapeHandlers({
28564
28595
  allowNewMessagesFromUnfilteredChannels,
28565
- filters,
28596
+ // `effectiveFilters`/`effectiveSort` reflect the backend-resolved
28597
+ // `predefined_filter` metadata when `options.predefined_filter` is in use.
28598
+ // For non-predefined queries they fall back to the caller-supplied
28599
+ // `filters`/`sort` props so behavior is unchanged.
28600
+ filters: effectiveFilters,
28566
28601
  lockChannelOrder,
28567
28602
  onAddedToChannel,
28568
28603
  onChannelDeleted,
@@ -28574,7 +28609,7 @@ const UnMemoizedChannelList = (props) => {
28574
28609
  onMessageNewHandler,
28575
28610
  onRemovedFromChannel,
28576
28611
  setChannels,
28577
- sort
28612
+ sort: effectiveSort
28578
28613
  // TODO: implement
28579
28614
  // customHandleChannelListShape
28580
28615
  });
@@ -30006,7 +30041,7 @@ const Notification = React.forwardRef(
30006
30041
  }
30007
30042
  removeNotification(notification.id);
30008
30043
  };
30009
- const isPersistent = !notification.duration;
30044
+ const isPersistent2 = !notification.duration;
30010
30045
  const severity = notification.severity;
30011
30046
  const livePriority = severity === "error" ? "assertive" : "polite";
30012
30047
  return /* @__PURE__ */ jsxRuntime.jsxs(
@@ -30052,7 +30087,7 @@ const Notification = React.forwardRef(
30052
30087
  },
30053
30088
  index
30054
30089
  )) }),
30055
- (showClose || isPersistent) && /* @__PURE__ */ jsxRuntime.jsx(
30090
+ (showClose || isPersistent2) && /* @__PURE__ */ jsxRuntime.jsx(
30056
30091
  useNotificationApi.Button,
30057
30092
  {
30058
30093
  appearance: "ghost",
@@ -30079,6 +30114,7 @@ const ENTER_TRANSLATION = {
30079
30114
  top: { x: "0%", y: "-100%" }
30080
30115
  };
30081
30116
  const EXIT_ANIMATION_MS = 340;
30117
+ const DEFAULT_MIN_DISPLAY_MS = 1e3;
30082
30118
  const isEnterFrom = (value) => value === "bottom" || value === "left" || value === "right" || value === "top";
30083
30119
  const getNotificationEnterFrom = (notification, fallbackEnterFrom) => {
30084
30120
  if (!notification) return fallbackEnterFrom;
@@ -30088,19 +30124,93 @@ const getNotificationEnterFrom = (notification, fallbackEnterFrom) => {
30088
30124
  if (isEnterFrom(originEnterFrom)) return originEnterFrom;
30089
30125
  return fallbackEnterFrom;
30090
30126
  };
30127
+ const isPersistent = (notification) => !notification.duration;
30128
+ const haveSameType = (a, b) => !!a?.type && !!b?.type && a.type === b.type;
30129
+ const pickOldest = (notifications, displayed) => {
30130
+ const excludeId = displayed?.id ?? null;
30131
+ let oldest = null;
30132
+ for (const notification of notifications) {
30133
+ if (notification.id === excludeId) continue;
30134
+ if (!oldest || notification.createdAt < oldest.createdAt) {
30135
+ oldest = notification;
30136
+ }
30137
+ }
30138
+ return oldest;
30139
+ };
30140
+ const pickNewest = (notifications, displayed) => {
30141
+ const excludeId = displayed?.id ?? null;
30142
+ let newest = null;
30143
+ for (const notification of notifications) {
30144
+ if (notification.id === excludeId) continue;
30145
+ if (!newest || notification.createdAt > newest.createdAt) {
30146
+ newest = notification;
30147
+ }
30148
+ }
30149
+ return newest;
30150
+ };
30151
+ const pickNewestPersistent = (notifications, excludeId) => {
30152
+ let newest = null;
30153
+ for (const notification of notifications) {
30154
+ if (notification.id === excludeId) continue;
30155
+ if (!isPersistent(notification)) continue;
30156
+ if (!newest || notification.createdAt > newest.createdAt) {
30157
+ newest = notification;
30158
+ }
30159
+ }
30160
+ return newest;
30161
+ };
30162
+ const pickNewestOfType = (notifications, type, excludeId) => {
30163
+ if (!type) return null;
30164
+ let newest = null;
30165
+ for (const notification of notifications) {
30166
+ if (notification.id === excludeId) continue;
30167
+ if (notification.type !== type) continue;
30168
+ if (!newest || notification.createdAt > newest.createdAt) {
30169
+ newest = notification;
30170
+ }
30171
+ }
30172
+ return newest;
30173
+ };
30174
+ const createDefaultPickNext = (pickFromQueue = pickOldest) => (notifications, displayed) => {
30175
+ if (notifications.length === 0) return null;
30176
+ const newestPersistent = pickNewestPersistent(notifications, null);
30177
+ if (!displayed) {
30178
+ return newestPersistent ?? pickFromQueue(notifications, null);
30179
+ }
30180
+ const displayedInStore = notifications.some(({ id }) => id === displayed.id);
30181
+ if (!displayedInStore) {
30182
+ return newestPersistent ?? pickFromQueue(notifications, null);
30183
+ }
30184
+ if (isPersistent(displayed)) {
30185
+ const newerPersistent = newestPersistent && newestPersistent.id !== displayed.id ? newestPersistent : pickNewestPersistent(notifications, displayed.id);
30186
+ if (newerPersistent && newerPersistent.createdAt > displayed.createdAt) {
30187
+ return newerPersistent;
30188
+ }
30189
+ return displayed;
30190
+ }
30191
+ const sameTypeNewest = pickNewestOfType(notifications, displayed.type, displayed.id);
30192
+ if (sameTypeNewest) return sameTypeNewest;
30193
+ if (newestPersistent) return newestPersistent;
30194
+ return pickFromQueue(notifications, displayed) ?? displayed;
30195
+ };
30196
+ const defaultPickNext = createDefaultPickNext(pickOldest);
30091
30197
  const NotificationList = ({
30092
30198
  className,
30093
30199
  enterFrom = "bottom",
30094
30200
  fallbackPanel,
30095
30201
  filter,
30202
+ minDisplayMs = DEFAULT_MIN_DISPLAY_MS,
30096
30203
  panel,
30204
+ pickNext = defaultPickNext,
30097
30205
  verticalAlignment = "bottom"
30098
30206
  }) => {
30099
30207
  const { Notification: NotificationComponent = Notification } = useNotificationApi.useComponentContext();
30100
30208
  const { t } = useNotificationApi.useTranslationContext();
30101
30209
  const { removeNotification, startNotificationTimeout } = useNotificationApi.useNotificationApi();
30102
30210
  const exitTimeoutRef = React.useRef(null);
30103
- const latestNotificationRef = React.useRef(null);
30211
+ const replacementTimeoutRef = React.useRef(null);
30212
+ const candidateRef = React.useRef(null);
30213
+ const displayedAtRef = React.useRef(null);
30104
30214
  const listRef = React.useRef(null);
30105
30215
  const observedElementRef = React.useRef(null);
30106
30216
  const startedTimeoutIdsRef = React.useRef(null);
@@ -30123,7 +30233,6 @@ const NotificationList = ({
30123
30233
  filter: combinedFilter,
30124
30234
  panel
30125
30235
  });
30126
- const nextNotification = notifications[0] ?? null;
30127
30236
  const dismiss = React.useCallback(
30128
30237
  (id) => {
30129
30238
  startedTimeoutIdsRef.current?.delete(id);
@@ -30139,33 +30248,105 @@ const NotificationList = ({
30139
30248
  }
30140
30249
  });
30141
30250
  }, [notifications]);
30142
- React.useEffect(() => {
30143
- latestNotificationRef.current = nextNotification;
30144
- }, [nextNotification]);
30251
+ const clearReplacementTimeout = React.useCallback(() => {
30252
+ if (replacementTimeoutRef.current !== null) {
30253
+ window.clearTimeout(replacementTimeoutRef.current);
30254
+ replacementTimeoutRef.current = null;
30255
+ }
30256
+ }, []);
30145
30257
  React.useEffect(
30146
30258
  () => () => {
30147
- if (exitTimeoutRef.current) {
30259
+ clearReplacementTimeout();
30260
+ if (exitTimeoutRef.current !== null) {
30148
30261
  window.clearTimeout(exitTimeoutRef.current);
30262
+ exitTimeoutRef.current = null;
30149
30263
  }
30150
30264
  },
30151
- []
30265
+ [clearReplacementTimeout]
30152
30266
  );
30153
30267
  React.useEffect(() => {
30268
+ candidateRef.current = pickNext(notifications, displayedNotification);
30269
+ }, [displayedNotification, notifications, pickNext]);
30270
+ React.useEffect(() => {
30271
+ if (transitionState === "exit") return;
30154
30272
  if (!displayedNotification) {
30155
- if (!nextNotification) return;
30156
- setDisplayedNotification(nextNotification);
30157
- setTransitionState("enter");
30273
+ const candidate2 = pickNext(notifications, null);
30274
+ if (candidate2) {
30275
+ displayedAtRef.current = Date.now();
30276
+ setDisplayedNotification(candidate2);
30277
+ setTransitionState("enter");
30278
+ }
30158
30279
  return;
30159
30280
  }
30160
- if (displayedNotification.id === nextNotification?.id) return;
30161
- if (transitionState === "exit") return;
30162
- setTransitionState("exit");
30163
- exitTimeoutRef.current = window.setTimeout(() => {
30164
- setDisplayedNotification(latestNotificationRef.current);
30165
- setTransitionState("enter");
30166
- exitTimeoutRef.current = null;
30167
- }, EXIT_ANIMATION_MS);
30168
- }, [displayedNotification, nextNotification, transitionState]);
30281
+ const candidate = pickNext(notifications, displayedNotification);
30282
+ if (!candidate) {
30283
+ clearReplacementTimeout();
30284
+ setTransitionState("exit");
30285
+ exitTimeoutRef.current = window.setTimeout(() => {
30286
+ exitTimeoutRef.current = null;
30287
+ displayedAtRef.current = null;
30288
+ setDisplayedNotification(null);
30289
+ setTransitionState("enter");
30290
+ }, EXIT_ANIMATION_MS);
30291
+ return;
30292
+ }
30293
+ if (candidate.id === displayedNotification.id) {
30294
+ clearReplacementTimeout();
30295
+ return;
30296
+ }
30297
+ const displayedInStore = notifications.some(
30298
+ ({ id }) => id === displayedNotification.id
30299
+ );
30300
+ const startSwap = () => {
30301
+ replacementTimeoutRef.current = null;
30302
+ setTransitionState("exit");
30303
+ const previousId = displayedNotification.id;
30304
+ const wasInStore = displayedInStore;
30305
+ exitTimeoutRef.current = window.setTimeout(() => {
30306
+ exitTimeoutRef.current = null;
30307
+ if (wasInStore) {
30308
+ startedTimeoutIdsRef.current?.delete(previousId);
30309
+ removeNotification(previousId);
30310
+ }
30311
+ const next = candidateRef.current;
30312
+ if (next && next.id !== previousId) {
30313
+ displayedAtRef.current = Date.now();
30314
+ setDisplayedNotification(next);
30315
+ setTransitionState("enter");
30316
+ } else {
30317
+ displayedAtRef.current = null;
30318
+ setDisplayedNotification(null);
30319
+ setTransitionState("enter");
30320
+ }
30321
+ }, EXIT_ANIMATION_MS);
30322
+ };
30323
+ if (!displayedInStore) {
30324
+ clearReplacementTimeout();
30325
+ startSwap();
30326
+ return;
30327
+ }
30328
+ if (haveSameType(displayedNotification, candidate)) {
30329
+ clearReplacementTimeout();
30330
+ startSwap();
30331
+ return;
30332
+ }
30333
+ const elapsed = displayedAtRef.current ? Date.now() - displayedAtRef.current : 0;
30334
+ const remaining = Math.max(0, minDisplayMs - elapsed);
30335
+ if (remaining === 0) {
30336
+ clearReplacementTimeout();
30337
+ startSwap();
30338
+ } else if (replacementTimeoutRef.current === null) {
30339
+ replacementTimeoutRef.current = window.setTimeout(startSwap, remaining);
30340
+ }
30341
+ }, [
30342
+ clearReplacementTimeout,
30343
+ displayedNotification,
30344
+ minDisplayMs,
30345
+ notifications,
30346
+ pickNext,
30347
+ removeNotification,
30348
+ transitionState
30349
+ ]);
30169
30350
  const notification = displayedNotification;
30170
30351
  const notificationEnterFrom = getNotificationEnterFrom(notification, enterFrom);
30171
30352
  React.useEffect(() => {
@@ -30647,7 +30828,7 @@ const useChat = ({
30647
30828
  };
30648
30829
  React.useEffect(() => {
30649
30830
  if (!client) return;
30650
- const version = "14.2.0";
30831
+ const version = "14.3.0";
30651
30832
  const userAgent = client.getUserAgent();
30652
30833
  if (!userAgent.includes("stream-chat-react")) {
30653
30834
  client.setUserAgent(`stream-chat-react-${version}-${userAgent}`);
@@ -31336,6 +31517,7 @@ exports.WaveProgressBar = WaveProgressBar;
31336
31517
  exports.Window = Window;
31337
31518
  exports.WithComponents = WithComponents;
31338
31519
  exports.WithDragAndDropUpload = WithDragAndDropUpload;
31520
+ exports.createDefaultPickNext = createDefaultPickNext;
31339
31521
  exports.deTranslations = deTranslations;
31340
31522
  exports.defaultAllowedTagNames = defaultAllowedTagNames;
31341
31523
  exports.defaultAttachmentActionsDefaultFocus = defaultAttachmentActionsDefaultFocus;
@@ -31358,6 +31540,7 @@ exports.frTranslations = frTranslations;
31358
31540
  exports.getChannelDisplayImage = getChannelDisplayImage;
31359
31541
  exports.getCssDimensionsVariables = getCssDimensionsVariables;
31360
31542
  exports.getGroupChannelDisplayInfo = getGroupChannelDisplayInfo;
31543
+ exports.getHasExtendedReactions = getHasExtendedReactions;
31361
31544
  exports.getLatestMessagePreview = getLatestMessagePreview;
31362
31545
  exports.getTextareaCaretRect = getTextareaCaretRect;
31363
31546
  exports.getTranslatedMessageText = getTranslatedMessageText;
@@ -31388,6 +31571,8 @@ exports.modalDialogId = modalDialogId;
31388
31571
  exports.modalDialogManagerId = modalDialogManagerId;
31389
31572
  exports.moveChannelUpwards = moveChannelUpwards;
31390
31573
  exports.nlTranslations = nlTranslations;
31574
+ exports.pickNewest = pickNewest;
31575
+ exports.pickOldest = pickOldest;
31391
31576
  exports.plusPlusToEmphasis = plusPlusToEmphasis;
31392
31577
  exports.ptTranslations = ptTranslations;
31393
31578
  exports.reactionHandlerWarning = reactionHandlerWarning;