stream-chat-react 12.7.1 → 12.8.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 (44) hide show
  1. package/dist/components/ChannelList/ChannelList.d.ts +1 -1
  2. package/dist/components/ChannelList/ChannelList.js +29 -34
  3. package/dist/components/ChannelList/hooks/index.d.ts +1 -0
  4. package/dist/components/ChannelList/hooks/index.js +1 -0
  5. package/dist/components/ChannelList/hooks/useChannelListShape.d.ts +60 -0
  6. package/dist/components/ChannelList/hooks/useChannelListShape.js +344 -0
  7. package/dist/components/ChannelList/hooks/useChannelMembershipState.d.ts +2 -0
  8. package/dist/components/ChannelList/hooks/useChannelMembershipState.js +15 -0
  9. package/dist/components/ChannelList/utils.d.ts +41 -5
  10. package/dist/components/ChannelList/utils.js +85 -0
  11. package/dist/components/ChannelPreview/ChannelPreviewActionButtons.d.ts +6 -0
  12. package/dist/components/ChannelPreview/ChannelPreviewActionButtons.js +30 -0
  13. package/dist/components/ChannelPreview/ChannelPreviewMessenger.d.ts +2 -2
  14. package/dist/components/ChannelPreview/ChannelPreviewMessenger.js +14 -9
  15. package/dist/components/ChannelPreview/icons.d.ts +6 -0
  16. package/dist/components/ChannelPreview/icons.js +8 -0
  17. package/dist/components/ChannelPreview/index.d.ts +1 -0
  18. package/dist/components/ChannelPreview/index.js +1 -0
  19. package/dist/components/Chat/hooks/useChat.js +1 -1
  20. package/dist/context/ComponentContext.d.ts +3 -1
  21. package/dist/css/v2/index.css +1 -1
  22. package/dist/css/v2/index.layout.css +1 -1
  23. package/dist/experimental/index.browser.cjs.map +2 -2
  24. package/dist/experimental/index.node.cjs.map +2 -2
  25. package/dist/i18n/Streami18n.d.ts +2 -0
  26. package/dist/i18n/de.json +2 -0
  27. package/dist/i18n/en.json +2 -0
  28. package/dist/i18n/es.json +2 -0
  29. package/dist/i18n/fr.json +2 -0
  30. package/dist/i18n/hi.json +2 -0
  31. package/dist/i18n/it.json +2 -0
  32. package/dist/i18n/ja.json +2 -0
  33. package/dist/i18n/ko.json +2 -0
  34. package/dist/i18n/nl.json +2 -0
  35. package/dist/i18n/pt.json +2 -0
  36. package/dist/i18n/ru.json +2 -0
  37. package/dist/i18n/tr.json +2 -0
  38. package/dist/index.browser.cjs +10313 -9728
  39. package/dist/index.browser.cjs.map +4 -4
  40. package/dist/index.node.cjs +10239 -9646
  41. package/dist/index.node.cjs.map +4 -4
  42. package/dist/scss/v2/ChannelPreview/ChannelPreview-layout.scss +24 -0
  43. package/dist/scss/v2/ChannelPreview/ChannelPreview-theme.scss +18 -0
  44. package/package.json +4 -4
@@ -1,5 +1,8 @@
1
1
  import uniqBy from 'lodash.uniqby';
2
2
  export const MAX_QUERY_CHANNELS_LIMIT = 30;
3
+ /**
4
+ * @deprecated
5
+ */
3
6
  export const moveChannelUp = ({ activeChannel, channels, cid, }) => {
4
7
  // get index of channel to move up
5
8
  const channelIndex = channels.findIndex((channel) => channel.cid === cid);
@@ -9,3 +12,85 @@ export const moveChannelUp = ({ activeChannel, channels, cid, }) => {
9
12
  const channel = activeChannel || channels[channelIndex];
10
13
  return uniqBy([channel, ...channels], 'cid');
11
14
  };
15
+ /**
16
+ * Expects channel array sorted by `{ pinned_at: -1 }`.
17
+ *
18
+ * TODO: add support for the `{ pinned_at: 1 }`
19
+ */
20
+ export function findLastPinnedChannelIndex({ channels, }) {
21
+ let lastPinnedChannelIndex = null;
22
+ for (const channel of channels) {
23
+ if (!isChannelPinned(channel))
24
+ break;
25
+ if (typeof lastPinnedChannelIndex === 'number') {
26
+ lastPinnedChannelIndex++;
27
+ }
28
+ else {
29
+ lastPinnedChannelIndex = 0;
30
+ }
31
+ }
32
+ return lastPinnedChannelIndex;
33
+ }
34
+ /**
35
+ * This function should not be used to move pinned already channels.
36
+ */
37
+ export const moveChannelUpwards = ({ channels, channelToMove, channelToMoveIndexWithinChannels, sort, }) => {
38
+ // get index of channel to move up
39
+ const targetChannelIndex = channelToMoveIndexWithinChannels ??
40
+ channels.findIndex((channel) => channel.cid === channelToMove.cid);
41
+ const targetChannelExistsWithinList = targetChannelIndex >= 0;
42
+ const targetChannelAlreadyAtTheTop = targetChannelIndex === 0;
43
+ // pinned channels should not move within the list based on recent activity, channels which
44
+ // receive messages and are not pinned should move upwards but only under the last pinned channel
45
+ // in the list
46
+ const considerPinnedChannels = shouldConsiderPinnedChannels(sort);
47
+ if (targetChannelAlreadyAtTheTop)
48
+ return channels;
49
+ const newChannels = [...channels];
50
+ // target channel index is known, remove it from the list
51
+ if (targetChannelExistsWithinList) {
52
+ newChannels.splice(targetChannelIndex, 1);
53
+ }
54
+ // as position of pinned channels has to stay unchanged, we need to
55
+ // find last pinned channel in the list to move the target channel after
56
+ let lastPinnedChannelIndex = null;
57
+ if (considerPinnedChannels) {
58
+ lastPinnedChannelIndex = findLastPinnedChannelIndex({ channels: newChannels });
59
+ }
60
+ // re-insert it at the new place (to specific index if pinned channels are considered)
61
+ newChannels.splice(typeof lastPinnedChannelIndex === 'number' ? lastPinnedChannelIndex + 1 : 0, 0, channelToMove);
62
+ return newChannels;
63
+ };
64
+ /**
65
+ * Returns true only if `{ pinned_at: -1 }` or `{ pinned_at: 1 }` option is first within the `sort` array.
66
+ */
67
+ export const shouldConsiderPinnedChannels = (sort) => {
68
+ if (!sort)
69
+ return false;
70
+ if (!Array.isArray(sort))
71
+ return false;
72
+ const [option] = sort;
73
+ if (!option?.pinned_at)
74
+ return false;
75
+ return Math.abs(option.pinned_at) === 1;
76
+ };
77
+ /**
78
+ * Returns `true` only if `archived` property is set to `false` within `filters`.
79
+ */
80
+ export const shouldConsiderArchivedChannels = (filters) => {
81
+ if (!filters)
82
+ return false;
83
+ return !filters.archived;
84
+ };
85
+ export const isChannelPinned = (channel) => {
86
+ if (!channel)
87
+ return false;
88
+ const member = channel.state.membership;
89
+ return !!member?.pinned_at;
90
+ };
91
+ export const isChannelArchived = (channel) => {
92
+ if (!channel)
93
+ return false;
94
+ const member = channel.state.membership;
95
+ return !!member?.archived_at;
96
+ };
@@ -0,0 +1,6 @@
1
+ import React from 'react';
2
+ import type { Channel, ExtendableGenerics } from 'stream-chat';
3
+ export type ChannelPreviewActionButtonsProps<SCG extends ExtendableGenerics> = {
4
+ channel: Channel<SCG>;
5
+ };
6
+ export declare function ChannelPreviewActionButtons<SCG extends ExtendableGenerics>({ channel, }: ChannelPreviewActionButtonsProps<SCG>): React.JSX.Element;
@@ -0,0 +1,30 @@
1
+ import React from 'react';
2
+ import clsx from 'clsx';
3
+ import { useChannelMembershipState } from '../ChannelList';
4
+ import { Icon } from './icons';
5
+ import { useTranslationContext } from '../../context';
6
+ export function ChannelPreviewActionButtons({ channel, }) {
7
+ const membership = useChannelMembershipState(channel);
8
+ const { t } = useTranslationContext();
9
+ return (React.createElement("div", { className: 'str-chat__channel-preview__action-buttons' },
10
+ React.createElement("button", { "aria-label": membership.pinned_at ? t('Unpin') : t('Pin'), className: clsx('str-chat__channel-preview__action-button', 'str-chat__channel-preview__action-button--pin', membership.pinned_at && 'str-chat__channel-preview__action-button--active'), onClick: (e) => {
11
+ e.stopPropagation();
12
+ if (membership.pinned_at) {
13
+ channel.unpin();
14
+ }
15
+ else {
16
+ channel.pin();
17
+ }
18
+ }, title: membership.pinned_at ? t('Unpin') : t('Pin') },
19
+ React.createElement(Icon.Pin, null)),
20
+ React.createElement("button", { "aria-label": membership.archived_at ? t('Unarchive') : t('Archive'), className: clsx('str-chat__channel-preview__action-button', 'str-chat__channel-preview__action-button--archive', membership.archived_at && 'str-chat__channel-preview__action-button--active'), onClick: (e) => {
21
+ e.stopPropagation();
22
+ if (membership.archived_at) {
23
+ channel.unarchive();
24
+ }
25
+ else {
26
+ channel.archive();
27
+ }
28
+ }, title: membership.archived_at ? t('Unarchive') : t('Archive') },
29
+ React.createElement(Icon.ArchiveBox, null))));
30
+ }
@@ -1,8 +1,8 @@
1
1
  import React from 'react';
2
- import type { ChannelPreviewUIComponentProps } from './ChannelPreview';
3
2
  import type { DefaultStreamChatGenerics } from '../../types/types';
3
+ import type { ChannelPreviewUIComponentProps } from './ChannelPreview';
4
4
  /**
5
5
  * Used as preview component for channel item in [ChannelList](#channellist) component.
6
6
  * Its best suited for messenger type chat.
7
7
  */
8
- export declare const ChannelPreviewMessenger: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>(props: ChannelPreviewUIComponentProps<StreamChatGenerics>) => React.JSX.Element;
8
+ export declare const ChannelPreviewMessenger: <SCG extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>(props: ChannelPreviewUIComponentProps<SCG>) => React.JSX.Element;
@@ -1,8 +1,11 @@
1
1
  import React, { useRef } from 'react';
2
2
  import clsx from 'clsx';
3
+ import { ChannelPreviewActionButtons as DefaultChannelPreviewActionButtons } from './ChannelPreviewActionButtons';
3
4
  import { Avatar as DefaultAvatar } from '../Avatar';
5
+ import { useComponentContext } from '../../context';
4
6
  const UnMemoizedChannelPreviewMessenger = (props) => {
5
7
  const { active, Avatar = DefaultAvatar, channel, className: customClassName = '', displayImage, displayTitle, groupChannelDisplayInfo, latestMessagePreview, onSelect: customOnSelectChannel, setActiveChannel, unread, watchers, } = props;
8
+ const { ChannelPreviewActionButtons = DefaultChannelPreviewActionButtons, } = useComponentContext();
6
9
  const channelPreviewButton = useRef(null);
7
10
  const avatarName = displayTitle || channel.state.messages[channel.state.messages.length - 1]?.user?.id;
8
11
  const onSelectChannel = (e) => {
@@ -16,15 +19,17 @@ const UnMemoizedChannelPreviewMessenger = (props) => {
16
19
  channelPreviewButton.current.blur();
17
20
  }
18
21
  };
19
- return (React.createElement("button", { "aria-label": `Select Channel: ${displayTitle || ''}`, "aria-selected": active, className: clsx(`str-chat__channel-preview-messenger str-chat__channel-preview`, active && 'str-chat__channel-preview-messenger--active', unread && unread >= 1 && 'str-chat__channel-preview-messenger--unread', customClassName), "data-testid": 'channel-preview-button', onClick: onSelectChannel, ref: channelPreviewButton, role: 'option' },
20
- React.createElement("div", { className: 'str-chat__channel-preview-messenger--left' },
21
- React.createElement(Avatar, { className: 'str-chat__avatar--channel-preview', groupChannelDisplayInfo: groupChannelDisplayInfo, image: displayImage, name: avatarName })),
22
- React.createElement("div", { className: 'str-chat__channel-preview-end' },
23
- React.createElement("div", { className: 'str-chat__channel-preview-end-first-row' },
24
- React.createElement("div", { className: 'str-chat__channel-preview-messenger--name' },
25
- React.createElement("span", null, displayTitle)),
26
- !!unread && (React.createElement("div", { className: 'str-chat__channel-preview-unread-badge', "data-testid": 'unread-badge' }, unread))),
27
- React.createElement("div", { className: 'str-chat__channel-preview-messenger--last-message' }, latestMessagePreview))));
22
+ return (React.createElement("div", { className: 'str-chat__channel-preview-container' },
23
+ React.createElement(ChannelPreviewActionButtons, { channel: channel }),
24
+ React.createElement("button", { "aria-label": `Select Channel: ${displayTitle || ''}`, "aria-selected": active, className: clsx(`str-chat__channel-preview-messenger str-chat__channel-preview`, active && 'str-chat__channel-preview-messenger--active', unread && unread >= 1 && 'str-chat__channel-preview-messenger--unread', customClassName), "data-testid": 'channel-preview-button', onClick: onSelectChannel, ref: channelPreviewButton, role: 'option' },
25
+ React.createElement("div", { className: 'str-chat__channel-preview-messenger--left' },
26
+ React.createElement(Avatar, { className: 'str-chat__avatar--channel-preview', groupChannelDisplayInfo: groupChannelDisplayInfo, image: displayImage, name: avatarName })),
27
+ React.createElement("div", { className: 'str-chat__channel-preview-end' },
28
+ React.createElement("div", { className: 'str-chat__channel-preview-end-first-row' },
29
+ React.createElement("div", { className: 'str-chat__channel-preview-messenger--name' },
30
+ React.createElement("span", null, displayTitle)),
31
+ !!unread && (React.createElement("div", { className: 'str-chat__channel-preview-unread-badge', "data-testid": 'unread-badge' }, unread))),
32
+ React.createElement("div", { className: 'str-chat__channel-preview-messenger--last-message' }, latestMessagePreview)))));
28
33
  };
29
34
  /**
30
35
  * Used as preview component for channel item in [ChannelList](#channellist) component.
@@ -0,0 +1,6 @@
1
+ import React from 'react';
2
+ import { ComponentPropsWithoutRef } from 'react';
3
+ export declare const Icon: {
4
+ ArchiveBox: (props: ComponentPropsWithoutRef<'svg'>) => React.JSX.Element;
5
+ Pin: (props: ComponentPropsWithoutRef<'svg'>) => React.JSX.Element;
6
+ };
@@ -0,0 +1,8 @@
1
+ /* eslint-disable react/display-name */
2
+ import React from 'react';
3
+ export const Icon = {
4
+ ArchiveBox: (props) => (React.createElement("svg", { className: 'str-chat__icon str-chat__icon--archive-box', fill: 'currentColor', viewBox: '0 0 512 512', xmlns: 'http://www.w3.org/2000/svg', ...props },
5
+ React.createElement("path", { d: 'M32 32l448 0c17.7 0 32 14.3 32 32l0 32c0 17.7-14.3 32-32 32L32 128C14.3 128 0 113.7 0 96L0 64C0 46.3 14.3 32 32 32zm0 128l448 0 0 256c0 35.3-28.7 64-64 64L96 480c-35.3 0-64-28.7-64-64l0-256zm128 80c0 8.8 7.2 16 16 16l160 0c8.8 0 16-7.2 16-16s-7.2-16-16-16l-160 0c-8.8 0-16 7.2-16 16z' }))),
6
+ Pin: (props) => (React.createElement("svg", { className: 'str-chat__icon str-chat__icon--pin', fill: 'currentColor', viewBox: '0 0 384 512', xmlns: 'http://www.w3.org/2000/svg', ...props },
7
+ React.createElement("path", { d: 'M32 32C32 14.3 46.3 0 64 0L320 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-29.5 0 11.4 148.2c36.7 19.9 65.7 53.2 79.5 94.7l1 3c3.3 9.8 1.6 20.5-4.4 28.8s-15.7 13.3-26 13.3L32 352c-10.3 0-19.9-4.9-26-13.3s-7.7-19.1-4.4-28.8l1-3c13.8-41.5 42.8-74.8 79.5-94.7L93.5 64 64 64C46.3 64 32 49.7 32 32zM160 384l64 0 0 96c0 17.7-14.3 32-32 32s-32-14.3-32-32l0-96z' }))),
8
+ };
@@ -1,4 +1,5 @@
1
1
  export * from './ChannelPreview';
2
2
  export * from './ChannelPreviewMessenger';
3
+ export * from './ChannelPreviewActionButtons';
3
4
  export * from './hooks';
4
5
  export * from './utils';
@@ -1,4 +1,5 @@
1
1
  export * from './ChannelPreview';
2
2
  export * from './ChannelPreviewMessenger';
3
+ export * from './ChannelPreviewActionButtons';
3
4
  export * from './hooks';
4
5
  export * from './utils';
@@ -28,7 +28,7 @@ export const useChat = ({ client, defaultLanguage = 'en', i18nInstance, initialN
28
28
  if (!userAgent.includes('stream-chat-react')) {
29
29
  // result looks like: 'stream-chat-react-2.3.2-stream-chat-javascript-client-browser-2.2.2'
30
30
  // the upper-case text between double underscores is replaced with the actual semantic version of the library
31
- client.setUserAgent(`stream-chat-react-12.7.1-${userAgent}`);
31
+ client.setUserAgent(`stream-chat-react-12.8.0-${userAgent}`);
32
32
  }
33
33
  client.threads.registerSubscriptions();
34
34
  client.polls.registerSubscriptions();
@@ -1,5 +1,5 @@
1
1
  import React, { PropsWithChildren } from 'react';
2
- import { AttachmentPreviewListProps, AttachmentProps, AvatarProps, BaseImageProps, CooldownTimerProps, CustomMessageActionsListProps, DateSeparatorProps, EmojiSearchIndex, EmptyStateIndicatorProps, EventComponentProps, FixedHeightMessageProps, GiphyPreviewMessageProps, LinkPreviewListProps, LoadingIndicatorProps, MessageBouncePromptProps, MessageDeletedProps, MessageInputProps, MessageListNotificationsProps, MessageNotificationProps, MessageOptionsProps, MessageProps, MessageRepliesCountButtonProps, MessageStatusProps, MessageTimestampProps, MessageUIComponentProps, ModalGalleryProps, PinIndicatorProps, PollCreationDialogProps, PollOptionSelectorProps, QuotedMessagePreviewProps, ReactionOptions, ReactionSelectorProps, ReactionsListProps, RecordingPermissionDeniedNotificationProps, SendButtonProps, StartRecordingAudioButtonProps, StreamedMessageTextProps, SuggestionItemProps, SuggestionListProps, ThreadHeaderProps, ThreadListItemProps, ThreadListItemUIProps, TimestampProps, TypingIndicatorProps, UnreadMessagesNotificationProps, UnreadMessagesSeparatorProps } from '../components';
2
+ import { AttachmentPreviewListProps, AttachmentProps, AvatarProps, BaseImageProps, ChannelPreviewActionButtonsProps, CooldownTimerProps, CustomMessageActionsListProps, DateSeparatorProps, EmojiSearchIndex, EmptyStateIndicatorProps, EventComponentProps, FixedHeightMessageProps, GiphyPreviewMessageProps, LinkPreviewListProps, LoadingIndicatorProps, MessageBouncePromptProps, MessageDeletedProps, MessageInputProps, MessageListNotificationsProps, MessageNotificationProps, MessageOptionsProps, MessageProps, MessageRepliesCountButtonProps, MessageStatusProps, MessageTimestampProps, MessageUIComponentProps, ModalGalleryProps, PinIndicatorProps, PollCreationDialogProps, PollOptionSelectorProps, QuotedMessagePreviewProps, ReactionOptions, ReactionSelectorProps, ReactionsListProps, RecordingPermissionDeniedNotificationProps, SendButtonProps, StartRecordingAudioButtonProps, StreamedMessageTextProps, SuggestionItemProps, SuggestionListProps, ThreadHeaderProps, ThreadListItemProps, ThreadListItemUIProps, TimestampProps, TypingIndicatorProps, UnreadMessagesNotificationProps, UnreadMessagesSeparatorProps } from '../components';
3
3
  import type { CustomTrigger, DefaultStreamChatGenerics, PropsWithChildrenOnly, UnknownType } from '../types/types';
4
4
  import type { StopAIGenerationButtonProps } from '../components/MessageInput/StopAIGenerationButton';
5
5
  export type ComponentContextValue<StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics, V extends CustomTrigger = CustomTrigger> = {
@@ -21,6 +21,8 @@ export type ComponentContextValue<StreamChatGenerics extends DefaultStreamChatGe
21
21
  Avatar?: React.ComponentType<AvatarProps<StreamChatGenerics>>;
22
22
  /** Custom UI component to display <img/> elements resp. a fallback in case of load error, defaults to and accepts same props as: [BaseImage](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Gallery/BaseImage.tsx) */
23
23
  BaseImage?: React.ComponentType<BaseImageProps>;
24
+ /** Custom UI component to display set of action buttons within `ChannelPreviewMessenger` component, accepts same props as: [ChannelPreviewActionButtons](https://github.com/GetStream/stream-chat-react/blob/master/src/components/ChannelList/ChannelPreviewActionButtons.tsx) */
25
+ ChannelPreviewActionButtons?: React.ComponentType<ChannelPreviewActionButtonsProps<StreamChatGenerics>>;
24
26
  /** Custom UI component to display the slow mode cooldown timer, defaults to and accepts same props as: [CooldownTimer](https://github.com/GetStream/stream-chat-react/blob/master/src/components/MessageInput/CooldownTimer.tsx) */
25
27
  CooldownTimer?: React.ComponentType<CooldownTimerProps>;
26
28
  /** Custom UI component to render set of buttons to be displayed in the MessageActionsBox, defaults to and accepts same props as: [CustomMessageActionsList](https://github.com/GetStream/stream-chat-react/blob/master/src/components/MessageActions/CustomMessageActionsList.tsx) */