stream-chat-react 12.11.1 → 12.12.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 (88) hide show
  1. package/dist/components/Channel/Channel.d.ts +1 -1
  2. package/dist/components/Channel/Channel.js +41 -26
  3. package/dist/components/Channel/channelState.d.ts +1 -1
  4. package/dist/components/Channel/channelState.js +2 -1
  5. package/dist/components/ChannelList/ChannelList.js +15 -9
  6. package/dist/components/ChannelPreview/ChannelPreview.d.ts +2 -2
  7. package/dist/components/ChannelPreview/ChannelPreview.js +3 -4
  8. package/dist/components/ChannelPreview/ChannelPreviewMessenger.d.ts +1 -1
  9. package/dist/components/ChannelSearch/SearchBar.js +7 -8
  10. package/dist/components/ChannelSearch/SearchResults.js +8 -7
  11. package/dist/components/Chat/Chat.d.ts +5 -2
  12. package/dist/components/Chat/Chat.js +12 -2
  13. package/dist/components/Chat/hooks/useChat.js +1 -1
  14. package/dist/components/Chat/hooks/useCreateChatContext.js +3 -1
  15. package/dist/components/InfiniteScrollPaginator/InfiniteScroll.d.ts +1 -1
  16. package/dist/components/InfiniteScrollPaginator/InfiniteScroll.js +1 -1
  17. package/dist/components/InfiniteScrollPaginator/InfiniteScrollPaginator.d.ts +1 -0
  18. package/dist/components/InfiniteScrollPaginator/InfiniteScrollPaginator.js +8 -2
  19. package/dist/components/MediaRecorder/classes/MediaRecorderController.d.ts +6 -1
  20. package/dist/components/MediaRecorder/classes/MediaRecorderController.js +6 -8
  21. package/dist/components/Message/renderText/renderText.d.ts +3 -3
  22. package/dist/components/Message/renderText/renderText.js +3 -3
  23. package/dist/context/ChatContext.d.ts +3 -1
  24. package/dist/context/ComponentContext.d.ts +23 -0
  25. package/dist/experimental/Search/Search.d.ts +12 -0
  26. package/dist/experimental/Search/Search.js +25 -0
  27. package/dist/experimental/Search/SearchBar/SearchBar.d.ts +2 -0
  28. package/dist/experimental/Search/SearchBar/SearchBar.js +56 -0
  29. package/dist/experimental/Search/SearchBar/index.d.ts +1 -0
  30. package/dist/experimental/Search/SearchBar/index.js +1 -0
  31. package/dist/experimental/Search/SearchContext.d.ts +23 -0
  32. package/dist/experimental/Search/SearchContext.js +10 -0
  33. package/dist/experimental/Search/SearchResults/SearchResultItem.d.ts +19 -0
  34. package/dist/experimental/Search/SearchResults/SearchResultItem.js +62 -0
  35. package/dist/experimental/Search/SearchResults/SearchResults.d.ts +3 -0
  36. package/dist/experimental/Search/SearchResults/SearchResults.js +21 -0
  37. package/dist/experimental/Search/SearchResults/SearchResultsHeader.d.ts +3 -0
  38. package/dist/experimental/Search/SearchResults/SearchResultsHeader.js +31 -0
  39. package/dist/experimental/Search/SearchResults/SearchResultsPresearch.d.ts +6 -0
  40. package/dist/experimental/Search/SearchResults/SearchResultsPresearch.js +6 -0
  41. package/dist/experimental/Search/SearchResults/SearchSourceResultList.d.ts +9 -0
  42. package/dist/experimental/Search/SearchResults/SearchSourceResultList.js +22 -0
  43. package/dist/experimental/Search/SearchResults/SearchSourceResultListFooter.d.ts +3 -0
  44. package/dist/experimental/Search/SearchResults/SearchSourceResultListFooter.js +16 -0
  45. package/dist/experimental/Search/SearchResults/SearchSourceResults.d.ts +7 -0
  46. package/dist/experimental/Search/SearchResults/SearchSourceResults.js +21 -0
  47. package/dist/experimental/Search/SearchResults/SearchSourceResultsEmpty.d.ts +2 -0
  48. package/dist/experimental/Search/SearchResults/SearchSourceResultsEmpty.js +6 -0
  49. package/dist/experimental/Search/SearchResults/SearchSourceResultsHeader.d.ts +1 -0
  50. package/dist/experimental/Search/SearchResults/SearchSourceResultsHeader.js +1 -0
  51. package/dist/experimental/Search/SearchResults/SearchSourceResultsLoadingIndicator.d.ts +2 -0
  52. package/dist/experimental/Search/SearchResults/SearchSourceResultsLoadingIndicator.js +8 -0
  53. package/dist/experimental/Search/SearchResults/index.d.ts +9 -0
  54. package/dist/experimental/Search/SearchResults/index.js +9 -0
  55. package/dist/experimental/Search/SearchSourceResultsContext.d.ts +13 -0
  56. package/dist/experimental/Search/SearchSourceResultsContext.js +10 -0
  57. package/dist/experimental/Search/hooks/index.d.ts +2 -0
  58. package/dist/experimental/Search/hooks/index.js +2 -0
  59. package/dist/experimental/Search/hooks/useSearchFocusedMessage.d.ts +2 -0
  60. package/dist/experimental/Search/hooks/useSearchFocusedMessage.js +8 -0
  61. package/dist/experimental/Search/hooks/useSearchQueriesInProgress.d.ts +6 -0
  62. package/dist/experimental/Search/hooks/useSearchQueriesInProgress.js +22 -0
  63. package/dist/experimental/Search/index.d.ts +5 -0
  64. package/dist/experimental/Search/index.js +5 -0
  65. package/dist/experimental/index.browser.cjs +11286 -301
  66. package/dist/experimental/index.browser.cjs.map +4 -4
  67. package/dist/experimental/index.d.ts +1 -0
  68. package/dist/experimental/index.js +1 -0
  69. package/dist/experimental/index.node.cjs +13176 -301
  70. package/dist/experimental/index.node.cjs.map +4 -4
  71. package/dist/i18n/Streami18n.d.ts +7 -0
  72. package/dist/i18n/de.json +7 -0
  73. package/dist/i18n/en.json +7 -0
  74. package/dist/i18n/es.json +7 -0
  75. package/dist/i18n/fr.json +8 -1
  76. package/dist/i18n/hi.json +7 -0
  77. package/dist/i18n/it.json +7 -0
  78. package/dist/i18n/ja.json +7 -0
  79. package/dist/i18n/ko.json +7 -0
  80. package/dist/i18n/nl.json +7 -0
  81. package/dist/i18n/pt.json +8 -1
  82. package/dist/i18n/ru.json +7 -0
  83. package/dist/i18n/tr.json +7 -0
  84. package/dist/index.browser.cjs +15820 -14852
  85. package/dist/index.browser.cjs.map +4 -4
  86. package/dist/index.node.cjs +17734 -16888
  87. package/dist/index.node.cjs.map +4 -4
  88. package/package.json +6 -7
@@ -1,5 +1,6 @@
1
1
  import React, { PropsWithChildren } from 'react';
2
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, ReactionsListModalProps, ReactionsListProps, RecordingPermissionDeniedNotificationProps, SendButtonProps, StartRecordingAudioButtonProps, StreamedMessageTextProps, SuggestionItemProps, SuggestionListProps, ThreadHeaderProps, ThreadListItemProps, ThreadListItemUIProps, TimestampProps, TypingIndicatorProps, UnreadMessagesNotificationProps, UnreadMessagesSeparatorProps } from '../components';
3
+ import { SearchProps, SearchResultsPresearchProps, SearchSourceResultListProps } from '../experimental';
3
4
  import type { CustomTrigger, DefaultStreamChatGenerics, PropsWithChildrenOnly, UnknownType } from '../types/types';
4
5
  import type { StopAIGenerationButtonProps } from '../components/MessageInput/StopAIGenerationButton';
5
6
  export type ComponentContextValue<StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics, V extends CustomTrigger = CustomTrigger> = {
@@ -108,6 +109,28 @@ export type ComponentContextValue<StreamChatGenerics extends DefaultStreamChatGe
108
109
  /** Custom UI component to display the reactions modal, defaults to and accepts same props as: [ReactionsListModal](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Reactions/ReactionsListModal.tsx) */
109
110
  ReactionsListModal?: React.ComponentType<ReactionsListModalProps<StreamChatGenerics>>;
110
111
  RecordingPermissionDeniedNotification?: React.ComponentType<RecordingPermissionDeniedNotificationProps>;
112
+ /** Custom component to display the search UI, defaults to and accepts same props as: [Search](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Search/Search.tsx) */
113
+ Search?: React.ComponentType<SearchProps>;
114
+ /** Custom component to display the UI where the searched string is entered, defaults to and accepts same props as: [SearchBar](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Search/SearchBar/SearchBar.tsx) */
115
+ SearchBar?: React.ComponentType;
116
+ /** Custom component for the search UI dedicated to display the results area, defaults to and accepts same props as: [SearchResults](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Search/SearchResults/SearchResults.tsx) */
117
+ SearchResults?: React.ComponentType;
118
+ /** Custom UI component to display header of search results pane, defaults to and accepts same props as: [SearchResultsHeader](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Search/SearchResults/SearchResultsHeader.tsx) */
119
+ SearchResultsHeader?: React.ComponentType;
120
+ /** Custom component to display search results pane before emitting the first search query for a given source, defaults to and accepts same props as: [SearchResultsPresearch](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Search/SearchResults/SearchSourceResultsPresearch.tsx) */
121
+ SearchResultsPresearch?: React.ComponentType<SearchResultsPresearchProps>;
122
+ /** Custom component to display the search source items results, defaults to and accepts same props as: [SearchSourceResultList](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Search/SearchResults/SearchSourceResultList.tsx) */
123
+ SearchSourceResultList?: React.ComponentType<SearchSourceResultListProps>;
124
+ /** Custom component to indicate the end of the last page for a searched source, defaults to and accepts same props as: [SearchSourceResultListFooter](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Search/SearchResults/SearchSourceResultListFooter.tsx) */
125
+ SearchSourceResultListFooter?: React.ComponentType;
126
+ /** Custom UI component to display search results items for a given search source pane, defaults to and accepts same props as: [SearchSourceResults](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Search/SearchResults/SourceSearchResults.tsx) */
127
+ SearchSourceResults?: React.ComponentType;
128
+ /** Custom component to display the search source results UI with 0 items found, defaults to and accepts same props as: [SearchSourceResultsEmpty](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Search/SearchResults/SearchSourceResultsEmpty.tsx) */
129
+ SearchSourceResultsEmpty?: React.ComponentType;
130
+ /** Custom component to display the header content for a given search source results, no default component is provided. */
131
+ SearchSourceResultsHeader?: React.ComponentType;
132
+ /** Custom component to display the search source results UI during the search query execution, defaults to and accepts same props as: [SearchSourceResultsLoadingIndicator](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Search/SearchResults/SearchSourceResultsLoadingIndicator.tsx) */
133
+ SearchSourceResultsLoadingIndicator?: React.ComponentType;
111
134
  /** Custom UI component for send button, defaults to and accepts same props as: [SendButton](https://github.com/GetStream/stream-chat-react/blob/master/src/components/MessageInput/icons.tsx) */
112
135
  SendButton?: React.ComponentType<SendButtonProps<StreamChatGenerics>>;
113
136
  /** Custom UI component button for initiating audio recording, defaults to and accepts same props as: [StartRecordingAudioButton](https://github.com/GetStream/stream-chat-react/blob/master/src/components/MediaRecorder/AudioRecorder/AudioRecordingButtons.tsx) */
@@ -0,0 +1,12 @@
1
+ import React from 'react';
2
+ import type { DefaultStreamChatGenerics } from '../../types';
3
+ export type SearchProps = {
4
+ directMessagingChannelType?: string;
5
+ /** Sets the input element into disabled state */
6
+ disabled?: boolean;
7
+ /** Clear search state / results on every click outside the search input, defaults to false */
8
+ exitSearchOnInputBlur?: boolean;
9
+ /** Custom placeholder text to be displayed in the search input */
10
+ placeholder?: string;
11
+ };
12
+ export declare const Search: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>({ directMessagingChannelType, disabled, exitSearchOnInputBlur, placeholder, }: SearchProps) => React.JSX.Element;
@@ -0,0 +1,25 @@
1
+ import clsx from 'clsx';
2
+ import React from 'react';
3
+ import { SearchBar as DefaultSearchBar } from './SearchBar/SearchBar';
4
+ import { SearchResults as DefaultSearchResults } from './SearchResults/SearchResults';
5
+ import { SearchContextProvider } from './SearchContext';
6
+ import { useChatContext, useComponentContext } from '../../context';
7
+ import { useStateStore } from '../../store';
8
+ const searchControllerStateSelector = (nextValue) => ({ isActive: nextValue.isActive });
9
+ export const Search = ({ directMessagingChannelType = 'messaging', disabled, exitSearchOnInputBlur, placeholder, }) => {
10
+ const { SearchBar = DefaultSearchBar, SearchResults = DefaultSearchResults } = useComponentContext();
11
+ const { searchController } = useChatContext();
12
+ const { isActive } = useStateStore(searchController.state, searchControllerStateSelector);
13
+ return (React.createElement(SearchContextProvider, { value: {
14
+ directMessagingChannelType,
15
+ disabled,
16
+ exitSearchOnInputBlur,
17
+ placeholder,
18
+ searchController,
19
+ } },
20
+ React.createElement("div", { className: clsx('str-chat__search', {
21
+ 'str-chat__search--active': isActive,
22
+ }), "data-testid": 'search' },
23
+ React.createElement(SearchBar, null),
24
+ React.createElement(SearchResults, null))));
25
+ };
@@ -0,0 +1,2 @@
1
+ import React from 'react';
2
+ export declare const SearchBar: () => React.JSX.Element;
@@ -0,0 +1,56 @@
1
+ import clsx from 'clsx';
2
+ import React, { useEffect, useState } from 'react';
3
+ import { useSearchContext } from '../SearchContext';
4
+ import { useSearchQueriesInProgress } from '../hooks';
5
+ import { useTranslationContext } from '../../../context';
6
+ import { useStateStore } from '../../../store';
7
+ const searchControllerStateSelector = (nextValue) => ({
8
+ isActive: nextValue.isActive,
9
+ searchQuery: nextValue.searchQuery,
10
+ });
11
+ export const SearchBar = () => {
12
+ const { t } = useTranslationContext();
13
+ const { disabled, exitSearchOnInputBlur, placeholder, searchController } = useSearchContext();
14
+ const queriesInProgress = useSearchQueriesInProgress(searchController);
15
+ const [input, setInput] = useState(null);
16
+ const { isActive, searchQuery } = useStateStore(searchController.state, searchControllerStateSelector);
17
+ useEffect(() => {
18
+ if (!input)
19
+ return;
20
+ const handleKeyDown = (event) => {
21
+ if (event.key === 'Escape') {
22
+ input.blur();
23
+ searchController.exit();
24
+ }
25
+ };
26
+ document.addEventListener('keydown', handleKeyDown);
27
+ return () => {
28
+ document.removeEventListener('keydown', handleKeyDown);
29
+ };
30
+ }, [searchController, input]);
31
+ return (React.createElement("div", { className: 'str-chat__search-bar', "data-testid": 'search-bar' },
32
+ React.createElement("div", { className: clsx('str-chat__search-input--wrapper', {
33
+ 'str-chat__search-input--wrapper-active': isActive,
34
+ }) },
35
+ React.createElement("div", { className: 'str-chat__search-input--icon' }),
36
+ React.createElement("input", { className: 'str-chat__search-input', "data-testid": 'search-input', disabled: disabled, onBlur: () => {
37
+ if (exitSearchOnInputBlur)
38
+ searchController.exit();
39
+ }, onChange: (event) => {
40
+ if (event.target.value) {
41
+ searchController.search(event.target.value);
42
+ }
43
+ else if (!event.target.value) {
44
+ searchController.clear();
45
+ }
46
+ }, onFocus: searchController.activate, placeholder: placeholder ?? t('Search'), ref: setInput, type: 'text', value: searchQuery }),
47
+ searchQuery && (React.createElement("button", { className: 'str-chat__search-input--clear-button', "data-testid": 'clear-input-button', disabled: queriesInProgress.length > 0, onClick: () => {
48
+ searchController.clear();
49
+ input?.focus();
50
+ } },
51
+ React.createElement("div", { className: 'str-chat__search-input--clear-button-icon' })))),
52
+ isActive ? (React.createElement("button", { className: clsx('str-chat__search-bar-button str-chat__search-bar-button--exit-search'), "data-testid": 'search-bar-button', onClick: () => {
53
+ input?.blur();
54
+ searchController.exit();
55
+ } }, t('Cancel'))) : null));
56
+ };
@@ -0,0 +1 @@
1
+ export * from './SearchBar';
@@ -0,0 +1 @@
1
+ export * from './SearchBar';
@@ -0,0 +1,23 @@
1
+ import React, { PropsWithChildren } from 'react';
2
+ import type { SearchController } from 'stream-chat';
3
+ import type { DefaultStreamChatGenerics } from '../../types';
4
+ export type SearchContextValue<StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics> = {
5
+ /** The type of channel to create on user result select, defaults to `messaging` */
6
+ directMessagingChannelType: string;
7
+ /** Instance of the search controller that handles the data management */
8
+ searchController: SearchController<StreamChatGenerics>;
9
+ /** Sets the input element into disabled state */
10
+ disabled?: boolean;
11
+ /** Clear search state / results on every click outside the search input, defaults to true */
12
+ exitSearchOnInputBlur?: boolean;
13
+ /** Custom placeholder text to be displayed in the search input */
14
+ placeholder?: string;
15
+ };
16
+ export declare const SearchContext: React.Context<SearchContextValue<DefaultStreamChatGenerics> | undefined>;
17
+ /**
18
+ * Context provider for components rendered within the `Search` component
19
+ */
20
+ export declare const SearchContextProvider: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>({ children, value, }: PropsWithChildren<{
21
+ value: SearchContextValue<StreamChatGenerics>;
22
+ }>) => React.JSX.Element;
23
+ export declare const useSearchContext: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>() => SearchContextValue<StreamChatGenerics>;
@@ -0,0 +1,10 @@
1
+ import React, { createContext, useContext } from 'react';
2
+ export const SearchContext = createContext(undefined);
3
+ /**
4
+ * Context provider for components rendered within the `Search` component
5
+ */
6
+ export const SearchContextProvider = ({ children, value, }) => (React.createElement(SearchContext.Provider, { value: value }, children));
7
+ export const useSearchContext = () => {
8
+ const contextValue = useContext(SearchContext);
9
+ return contextValue;
10
+ };
@@ -0,0 +1,19 @@
1
+ import React, { ComponentType } from 'react';
2
+ import type { Channel, MessageResponse, User } from 'stream-chat';
3
+ import type { DefaultStreamChatGenerics } from '../../../types';
4
+ export type ChannelSearchResultItemProps<StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics> = {
5
+ item: Channel<StreamChatGenerics>;
6
+ };
7
+ export declare const ChannelSearchResultItem: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>({ item, }: ChannelSearchResultItemProps<StreamChatGenerics>) => React.JSX.Element;
8
+ export type ChannelByMessageSearchResultItemProps<StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics> = {
9
+ item: MessageResponse<StreamChatGenerics>;
10
+ };
11
+ export declare const MessageSearchResultItem: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>({ item, }: ChannelByMessageSearchResultItemProps<StreamChatGenerics>) => React.JSX.Element | undefined;
12
+ export type UserSearchResultItemProps<StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics> = {
13
+ item: User<StreamChatGenerics>;
14
+ };
15
+ export declare const UserSearchResultItem: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>({ item, }: UserSearchResultItemProps<StreamChatGenerics>) => React.JSX.Element;
16
+ export type SearchResultItemComponents = Record<string, ComponentType<{
17
+ item: any;
18
+ }>>;
19
+ export declare const DefaultSearchResultItems: SearchResultItemComponents;
@@ -0,0 +1,62 @@
1
+ import uniqBy from 'lodash.uniqby';
2
+ import React, { useCallback, useMemo } from 'react';
3
+ import { useSearchContext } from '../SearchContext';
4
+ import { Avatar } from '../../../components/Avatar';
5
+ import { ChannelPreview } from '../../../components/ChannelPreview';
6
+ import { useChannelListContext, useChatContext } from '../../../context';
7
+ import { DEFAULT_JUMP_TO_PAGE_SIZE } from '../../../constants/limits';
8
+ export const ChannelSearchResultItem = ({ item, }) => {
9
+ const { setActiveChannel } = useChatContext();
10
+ const { setChannels } = useChannelListContext();
11
+ const onSelect = useCallback(() => {
12
+ setActiveChannel(item);
13
+ setChannels?.((channels) => uniqBy([item, ...channels], 'cid'));
14
+ }, [item, setActiveChannel, setChannels]);
15
+ return (React.createElement(ChannelPreview, { channel: item, className: 'str-chat__search-result', onSelect: onSelect }));
16
+ };
17
+ export const MessageSearchResultItem = ({ item, }) => {
18
+ const { channel: activeChannel, client, searchController, setActiveChannel, } = useChatContext();
19
+ const { setChannels } = useChannelListContext();
20
+ const channel = useMemo(() => {
21
+ const { channel: channelData } = item;
22
+ const type = channelData?.type ?? 'unknown';
23
+ const id = channelData?.id ?? 'unknown';
24
+ return client.channel(type, id);
25
+ }, [client, item]);
26
+ const onSelect = useCallback(async () => {
27
+ if (!channel)
28
+ return;
29
+ await channel.state.loadMessageIntoState(item.id, undefined, DEFAULT_JUMP_TO_PAGE_SIZE);
30
+ // FIXME: message focus should be handled by yet non-existent msg list controller in client packaged
31
+ searchController._internalState.partialNext({ focusedMessage: item });
32
+ setActiveChannel(channel);
33
+ setChannels?.((channels) => uniqBy([channel, ...channels], 'cid'));
34
+ }, [channel, item, searchController, setActiveChannel, setChannels]);
35
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
36
+ const getLatestMessagePreview = useCallback(() => item.text, [item]);
37
+ if (!channel)
38
+ return;
39
+ return (React.createElement(ChannelPreview, { active: channel.cid === activeChannel?.cid &&
40
+ item.id === searchController._internalState.getLatestValue().focusedMessage?.id, channel: channel, className: 'str-chat__search-result', getLatestMessagePreview: getLatestMessagePreview, onSelect: onSelect }));
41
+ };
42
+ export const UserSearchResultItem = ({ item, }) => {
43
+ const { client, setActiveChannel } = useChatContext();
44
+ const { setChannels } = useChannelListContext();
45
+ const { directMessagingChannelType } = useSearchContext();
46
+ const onClick = useCallback(() => {
47
+ const newChannel = client.channel(directMessagingChannelType, {
48
+ members: [client.userID, item.id],
49
+ });
50
+ newChannel.watch();
51
+ setActiveChannel(newChannel);
52
+ setChannels?.((channels) => uniqBy([newChannel, ...channels], 'cid'));
53
+ }, [client, item, setActiveChannel, setChannels, directMessagingChannelType]);
54
+ return (React.createElement("button", { "aria-label": `Select User Channel: ${item.name || ''}`, className: 'str-chat__search-result', "data-testid": 'search-result-user', onClick: onClick, role: 'option' },
55
+ React.createElement(Avatar, { className: 'str-chat__avatar--channel-preview', image: item.image, name: item.name || item.id, user: item }),
56
+ React.createElement("div", { className: 'str-chat__search-result--display-name' }, item.name || item.id)));
57
+ };
58
+ export const DefaultSearchResultItems = {
59
+ channels: ChannelSearchResultItem,
60
+ messages: MessageSearchResultItem,
61
+ users: UserSearchResultItem,
62
+ };
@@ -0,0 +1,3 @@
1
+ import React from 'react';
2
+ import type { DefaultStreamChatGenerics } from '../../../types';
3
+ export declare const SearchResults: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>() => React.JSX.Element | null;
@@ -0,0 +1,21 @@
1
+ import React from 'react';
2
+ import { SearchSourceResults as DefaultSourceSearchResults } from './SearchSourceResults';
3
+ import { SearchResultsHeader as DefaultSearchResultsHeader } from './SearchResultsHeader';
4
+ import { SearchResultsPresearch as DefaultSearchResultsPresearch } from './SearchResultsPresearch';
5
+ import { useSearchContext } from '../SearchContext';
6
+ import { useComponentContext, useTranslationContext } from '../../../context';
7
+ import { useStateStore } from '../../../store';
8
+ const searchControllerStateSelector = (nextValue) => ({
9
+ activeSources: nextValue.sources.filter((s) => s.isActive),
10
+ isActive: nextValue.isActive,
11
+ searchQuery: nextValue.searchQuery,
12
+ });
13
+ export const SearchResults = () => {
14
+ const { t } = useTranslationContext('ResultsContainer');
15
+ const { SearchResultsHeader = DefaultSearchResultsHeader, SearchResultsPresearch = DefaultSearchResultsPresearch, SearchSourceResults = DefaultSourceSearchResults, } = useComponentContext();
16
+ const { searchController } = useSearchContext();
17
+ const { activeSources, isActive, searchQuery } = useStateStore(searchController.state, searchControllerStateSelector);
18
+ return !isActive ? null : (React.createElement("div", { "aria-label": t('aria/Search results'), className: 'str-chat__search-results' },
19
+ React.createElement(SearchResultsHeader, null),
20
+ !searchQuery ? (React.createElement(SearchResultsPresearch, { activeSources: activeSources })) : (activeSources.map((source) => (React.createElement(SearchSourceResults, { key: source.type, searchSource: source }))))));
21
+ };
@@ -0,0 +1,3 @@
1
+ import React from 'react';
2
+ import type { DefaultStreamChatGenerics } from '../../../types';
3
+ export declare const SearchResultsHeader: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>() => React.JSX.Element;
@@ -0,0 +1,31 @@
1
+ import clsx from 'clsx';
2
+ import React from 'react';
3
+ import { useSearchContext } from '../SearchContext';
4
+ import { useTranslationContext } from '../../../context';
5
+ import { useStateStore } from '../../../store';
6
+ const searchSourceStateSelector = (nextValue) => ({
7
+ isActive: nextValue.isActive,
8
+ });
9
+ const SearchSourceFilterButton = ({ source, }) => {
10
+ const { t } = useTranslationContext();
11
+ const { searchController } = useSearchContext();
12
+ const { isActive } = useStateStore(source.state, searchSourceStateSelector);
13
+ const label = `search-results-header-filter-source-button-label--${source.type}`;
14
+ return (React.createElement("button", { "aria-label": t('aria/Search results header filter button'), className: clsx('str-chat__search-results-header__filter-source-button', {
15
+ 'str-chat__search-results-header__filter-source-button--active': isActive,
16
+ }), key: label, onClick: () => {
17
+ if (source.isActive) {
18
+ searchController.deactivateSource(source.type);
19
+ }
20
+ else {
21
+ searchController.activateSource(source.type);
22
+ if (searchController.searchQuery && !source.items?.length)
23
+ source.search(searchController.searchQuery);
24
+ }
25
+ } }, t(label)));
26
+ };
27
+ export const SearchResultsHeader = () => {
28
+ const { searchController } = useSearchContext();
29
+ return (React.createElement("div", { className: 'str-chat__search-results-header', "data-testid": 'search-results-header' },
30
+ React.createElement("div", { className: 'str-chat__search-results-header__filter-source-buttons', "data-testid": 'filter-source-buttons' }, searchController.sources.map((source) => (React.createElement(SearchSourceFilterButton, { key: `search-source-filter-button-${source.type}`, source: source }))))));
31
+ };
@@ -0,0 +1,6 @@
1
+ import React from 'react';
2
+ import type { SearchSource } from 'stream-chat';
3
+ export type SearchResultsPresearchProps = {
4
+ activeSources: SearchSource[];
5
+ };
6
+ export declare const SearchResultsPresearch: () => React.JSX.Element;
@@ -0,0 +1,6 @@
1
+ import React from 'react';
2
+ import { useTranslationContext } from '../../../context';
3
+ export const SearchResultsPresearch = () => {
4
+ const { t } = useTranslationContext();
5
+ return (React.createElement("div", { className: 'str-chat__search-results-presearch' }, t('Start typing to search')));
6
+ };
@@ -0,0 +1,9 @@
1
+ import React from 'react';
2
+ import { SearchResultItemComponents } from './SearchResultItem';
3
+ import type { DefaultStreamChatGenerics } from '../../../types';
4
+ export type SearchSourceResultListProps = {
5
+ loadMoreDebounceMs?: number;
6
+ loadMoreThresholdPx?: number;
7
+ SearchResultItems?: SearchResultItemComponents;
8
+ };
9
+ export declare const SearchSourceResultList: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>({ loadMoreDebounceMs, loadMoreThresholdPx, SearchResultItems, }: SearchSourceResultListProps) => React.JSX.Element | null;
@@ -0,0 +1,22 @@
1
+ import React from 'react';
2
+ import { DefaultSearchResultItems } from './SearchResultItem';
3
+ import { SearchSourceResultListFooter as DefaultSearchSourceResultListFooter } from './SearchSourceResultListFooter';
4
+ import { useSearchSourceResultsContext } from '../SearchSourceResultsContext';
5
+ import { InfiniteScrollPaginator } from '../../../components/InfiniteScrollPaginator/InfiniteScrollPaginator';
6
+ import { useComponentContext } from '../../../context';
7
+ import { useStateStore } from '../../../store';
8
+ const searchSourceStateSelector = (nextValue) => ({
9
+ items: nextValue.items,
10
+ });
11
+ export const SearchSourceResultList = ({ loadMoreDebounceMs = 100, loadMoreThresholdPx = 80, SearchResultItems = DefaultSearchResultItems, }) => {
12
+ const { SearchSourceResultListFooter = DefaultSearchSourceResultListFooter } = useComponentContext();
13
+ const { searchSource } = useSearchSourceResultsContext();
14
+ const { items } = useStateStore(searchSource.state, searchSourceStateSelector);
15
+ const SearchResultItem = SearchResultItems[searchSource.type];
16
+ if (!SearchResultItem)
17
+ return null;
18
+ return (React.createElement("div", { className: 'str-chat__search-source-result-list', "data-testid": 'search-source-result-list' },
19
+ React.createElement(InfiniteScrollPaginator, { loadNextDebounceMs: loadMoreDebounceMs, loadNextOnScrollToBottom: searchSource.search, threshold: loadMoreThresholdPx },
20
+ items?.map((item, i) => (React.createElement(SearchResultItem, { item: item, key: `source-search-result-${searchSource.type}-${i}` }))),
21
+ React.createElement(SearchSourceResultListFooter, null))));
22
+ };
@@ -0,0 +1,3 @@
1
+ import React from 'react';
2
+ import type { DefaultStreamChatGenerics } from '../../../types';
3
+ export declare const SearchSourceResultListFooter: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>() => React.JSX.Element;
@@ -0,0 +1,16 @@
1
+ import React from 'react';
2
+ import { SearchSourceResultsLoadingIndicator as DefaultSearchSourceResultsLoadingIndicator } from './SearchSourceResultsLoadingIndicator';
3
+ import { useSearchSourceResultsContext } from '../SearchSourceResultsContext';
4
+ import { useComponentContext, useTranslationContext } from '../../../context';
5
+ import { useStateStore } from '../../../store';
6
+ const searchSourceStateSelector = (value) => ({
7
+ hasNext: value.hasNext,
8
+ isLoading: value.isLoading,
9
+ });
10
+ export const SearchSourceResultListFooter = () => {
11
+ const { t } = useTranslationContext();
12
+ const { SearchSourceResultsLoadingIndicator = DefaultSearchSourceResultsLoadingIndicator, } = useComponentContext();
13
+ const { searchSource } = useSearchSourceResultsContext();
14
+ const { hasNext, isLoading } = useStateStore(searchSource.state, searchSourceStateSelector);
15
+ return (React.createElement("div", { className: 'str-chat__search-source-result-list__footer', "data-testid": 'search-footer' }, isLoading ? (React.createElement(SearchSourceResultsLoadingIndicator, null)) : !hasNext ? (React.createElement("div", { className: 'str-chat__search-source-results---empty' }, t('All results loaded'))) : null));
16
+ };
@@ -0,0 +1,7 @@
1
+ import React from 'react';
2
+ import type { SearchSource } from 'stream-chat';
3
+ import type { DefaultStreamChatGenerics } from '../../../types';
4
+ export type SearchSourceResultsProps = {
5
+ searchSource: SearchSource;
6
+ };
7
+ export declare const SearchSourceResults: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>({ searchSource, }: SearchSourceResultsProps) => React.JSX.Element | null;
@@ -0,0 +1,21 @@
1
+ import React from 'react';
2
+ import { SearchSourceResultList as DefaultSearchSourceResultList } from './SearchSourceResultList';
3
+ import { SearchSourceResultsEmpty as DefaultSearchSourceResultsEmpty } from './SearchSourceResultsEmpty';
4
+ import { SearchSourceResultsHeader as DefaultSearchSourceResultsHeader } from './SearchSourceResultsHeader';
5
+ import { SearchSourceResultsContextProvider } from '../SearchSourceResultsContext';
6
+ import { useComponentContext } from '../../../context';
7
+ import { useStateStore } from '../../../store';
8
+ const searchSourceStateSelector = (nextValue) => ({
9
+ isLoading: nextValue.isLoading,
10
+ items: nextValue.items,
11
+ });
12
+ export const SearchSourceResults = ({ searchSource, }) => {
13
+ const { SearchSourceResultList = DefaultSearchSourceResultList, SearchSourceResultsEmpty = DefaultSearchSourceResultsEmpty, SearchSourceResultsHeader = DefaultSearchSourceResultsHeader, } = useComponentContext();
14
+ const { isLoading, items } = useStateStore(searchSource.state, searchSourceStateSelector);
15
+ if (!items && !isLoading)
16
+ return null;
17
+ return (React.createElement(SearchSourceResultsContextProvider, { value: { searchSource } },
18
+ React.createElement("div", { className: 'str-chat__search-source-results', "data-testid": 'search-source-results' },
19
+ React.createElement(SearchSourceResultsHeader, null),
20
+ items?.length || isLoading ? (React.createElement(SearchSourceResultList, null)) : (React.createElement(SearchSourceResultsEmpty, null)))));
21
+ };
@@ -0,0 +1,2 @@
1
+ import React from 'react';
2
+ export declare const SearchSourceResultsEmpty: () => React.JSX.Element;
@@ -0,0 +1,6 @@
1
+ import React from 'react';
2
+ import { useTranslationContext } from '../../../context';
3
+ export const SearchSourceResultsEmpty = () => {
4
+ const { t } = useTranslationContext();
5
+ return (React.createElement("div", { className: 'str-chat__search-source-results-empty' }, t('No results found')));
6
+ };
@@ -0,0 +1 @@
1
+ export declare const SearchSourceResultsHeader: () => null;
@@ -0,0 +1 @@
1
+ export const SearchSourceResultsHeader = () => null;
@@ -0,0 +1,2 @@
1
+ import React from 'react';
2
+ export declare const SearchSourceResultsLoadingIndicator: () => React.JSX.Element;
@@ -0,0 +1,8 @@
1
+ import React from 'react';
2
+ import { useTranslationContext } from '../../../context';
3
+ import { useSearchSourceResultsContext } from '../SearchSourceResultsContext';
4
+ export const SearchSourceResultsLoadingIndicator = () => {
5
+ const { t } = useTranslationContext();
6
+ const { searchSource } = useSearchSourceResultsContext();
7
+ return (React.createElement("div", { className: 'str-chat__search-source-results__loading-indicator', "data-testid": 'search-loading-indicator' }, t(`Searching for ${searchSource.type}...`)));
8
+ };
@@ -0,0 +1,9 @@
1
+ export * from './SearchResultItem';
2
+ export * from './SearchResults';
3
+ export * from './SearchResultsHeader';
4
+ export * from './SearchResultsPresearch';
5
+ export * from './SearchSourceResultsEmpty';
6
+ export * from './SearchSourceResultsLoadingIndicator';
7
+ export * from './SearchSourceResultList';
8
+ export * from './SearchSourceResultListFooter';
9
+ export * from './SearchSourceResults';
@@ -0,0 +1,9 @@
1
+ export * from './SearchResultItem';
2
+ export * from './SearchResults';
3
+ export * from './SearchResultsHeader';
4
+ export * from './SearchResultsPresearch';
5
+ export * from './SearchSourceResultsEmpty';
6
+ export * from './SearchSourceResultsLoadingIndicator';
7
+ export * from './SearchSourceResultList';
8
+ export * from './SearchSourceResultListFooter';
9
+ export * from './SearchSourceResults';
@@ -0,0 +1,13 @@
1
+ import React, { PropsWithChildren } from 'react';
2
+ import type { SearchSource } from 'stream-chat';
3
+ export type SearchSourceResultsContextValue = {
4
+ searchSource: SearchSource;
5
+ };
6
+ export declare const SearchSourceResultsContext: React.Context<SearchSourceResultsContextValue | undefined>;
7
+ /**
8
+ * Context provider for components rendered within the `SearchSourceResults`
9
+ */
10
+ export declare const SearchSourceResultsContextProvider: ({ children, value, }: PropsWithChildren<{
11
+ value: SearchSourceResultsContextValue;
12
+ }>) => React.JSX.Element;
13
+ export declare const useSearchSourceResultsContext: () => SearchSourceResultsContextValue;
@@ -0,0 +1,10 @@
1
+ import React, { createContext, useContext } from 'react';
2
+ export const SearchSourceResultsContext = createContext(undefined);
3
+ /**
4
+ * Context provider for components rendered within the `SearchSourceResults`
5
+ */
6
+ export const SearchSourceResultsContextProvider = ({ children, value, }) => (React.createElement(SearchSourceResultsContext.Provider, { value: value }, children));
7
+ export const useSearchSourceResultsContext = () => {
8
+ const contextValue = useContext(SearchSourceResultsContext);
9
+ return contextValue;
10
+ };
@@ -0,0 +1,2 @@
1
+ export * from './useSearchFocusedMessage';
2
+ export * from './useSearchQueriesInProgress';
@@ -0,0 +1,2 @@
1
+ export * from './useSearchFocusedMessage';
2
+ export * from './useSearchQueriesInProgress';
@@ -0,0 +1,2 @@
1
+ import type { DefaultStreamChatGenerics } from '../../../types';
2
+ export declare const useSearchFocusedMessage: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>() => import("stream-chat").MessageResponse<StreamChatGenerics> | undefined;
@@ -0,0 +1,8 @@
1
+ import { useChatContext } from '../../../context';
2
+ import { useStateStore } from '../../../store';
3
+ const searchControllerStateSelector = (nextValue) => ({ focusedMessage: nextValue.focusedMessage });
4
+ export const useSearchFocusedMessage = () => {
5
+ const { searchController } = useChatContext('Channel');
6
+ const { focusedMessage } = useStateStore(searchController._internalState, searchControllerStateSelector);
7
+ return focusedMessage;
8
+ };
@@ -0,0 +1,6 @@
1
+ import type { SearchController } from 'stream-chat';
2
+ import type { DefaultStreamChatGenerics } from '../../../types';
3
+ export type UseSearchQueriesInProgressParams<StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics> = {
4
+ searchController: SearchController<StreamChatGenerics>;
5
+ };
6
+ export declare const useSearchQueriesInProgress: <StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics>(searchController: SearchController<StreamChatGenerics>) => string[];
@@ -0,0 +1,22 @@
1
+ import { useEffect, useState } from 'react';
2
+ import { useStateStore } from '../../../store';
3
+ const searchControllerStateSelector = (value) => ({
4
+ sources: value.sources,
5
+ });
6
+ export const useSearchQueriesInProgress = (searchController) => {
7
+ const [queriesInProgress, setQueriesInProgress] = useState([]);
8
+ const { sources } = useStateStore(searchController.state, searchControllerStateSelector);
9
+ useEffect(() => {
10
+ const subscriptions = sources.map((source) => source.state.subscribeWithSelector((value) => ({ isLoading: value.isLoading }), ({ isLoading }) => {
11
+ setQueriesInProgress((prev) => {
12
+ if (isLoading)
13
+ return prev.concat(source.type);
14
+ return prev.filter((type) => type !== source.type);
15
+ });
16
+ }));
17
+ return () => {
18
+ subscriptions.forEach((unsubscribe) => unsubscribe());
19
+ };
20
+ }, [sources]);
21
+ return queriesInProgress;
22
+ };
@@ -0,0 +1,5 @@
1
+ export * from './Search';
2
+ export * from './SearchBar';
3
+ export * from './SearchContext';
4
+ export * from './SearchResults';
5
+ export * from './SearchSourceResultsContext';
@@ -0,0 +1,5 @@
1
+ export * from './Search';
2
+ export * from './SearchBar';
3
+ export * from './SearchContext';
4
+ export * from './SearchResults';
5
+ export * from './SearchSourceResultsContext';