diff --git a/examples/vite/src/ChatLayout/Panels.tsx b/examples/vite/src/ChatLayout/Panels.tsx index 36001a2b87..e4b3d4aba3 100644 --- a/examples/vite/src/ChatLayout/Panels.tsx +++ b/examples/vite/src/ChatLayout/Panels.tsx @@ -63,14 +63,20 @@ export const ChannelsPanels = ({ })} ref={channelsLayoutRef} > - + + + diff --git a/src/components/ChannelList/ChannelList.tsx b/src/components/ChannelList/ChannelList.tsx index f9e43757df..8f2ce3c7b3 100644 --- a/src/components/ChannelList/ChannelList.tsx +++ b/src/components/ChannelList/ChannelList.tsx @@ -19,16 +19,11 @@ import { usePrepareShapeHandlers, } from './hooks/useChannelListShape'; import { useStateStore } from '../../store'; -import type { ChannelListMessengerProps } from './ChannelListMessenger'; -import { ChannelListMessenger } from './ChannelListMessenger'; -import type { ChannelAvatarProps } from '../Avatar'; -import { Avatar as DefaultAvatar } from '../Avatar'; -import type { ChannelListItemUIProps } from '../ChannelListItem/ChannelListItem'; +import { ChannelListUI as DefaultChannelListUI } from './ChannelListUI'; import { ChannelListItem } from '../ChannelListItem/ChannelListItem'; import { Search as DefaultSearch } from '../Search'; import type { EmptyStateIndicatorProps } from '../EmptyStateIndicator'; import { EmptyStateIndicator as DefaultEmptyStateIndicator } from '../EmptyStateIndicator'; -import { LoadingChannels } from '../Loading/LoadingChannels'; import type { LoadMorePaginatorProps } from '../LoadMore/LoadMorePaginator'; import { LoadMorePaginator } from '../LoadMore/LoadMorePaginator'; import { NotificationList as DefaultNotificationList } from '../Notifications'; @@ -39,11 +34,9 @@ import { useChatContext, useComponentContext, } from '../../context'; -import { NullComponent } from '../UtilityComponents'; import { moveChannelUpwards } from './utils'; import type { TranslationContextValue } from '../../context/TranslationContext'; import type { PaginatorProps } from '../../types/types'; -import type { LoadingErrorIndicatorProps } from '../Loading'; import { ChannelListHeader } from './ChannelListHeader'; import { useStableId } from '../UtilityComponents/useStableId'; @@ -63,8 +56,6 @@ export type ChannelListProps = { * to false, which will prevent channels not in the list from incrementing the list. The default is true. */ allowNewMessagesFromUnfilteredChannels?: boolean; - /** UI component to display an avatar, defaults to [Avatar](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Avatar/Avatar.tsx) component and accepts the same props as: [ChannelAvatar](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Avatar/ChannelAvatar.tsx) */ - Avatar?: React.ComponentType; /** Optional function to filter channels prior to loading in the DOM. Do not use any complex or async logic that would delay the loading of the ChannelList. We recommend using a pure function with array methods like filter/sort/reduce. */ channelRenderFilterFn?: (channels: Array) => Array; // FIXME: how is this even legal (WHY IS IT STRING?!) @@ -83,12 +74,6 @@ export type ChannelListProps = { userLanguage: TranslationContextValue['userLanguage'], isMessageAIGenerated?: ChatContextValue['isMessageAIGenerated'], ) => ReactNode; - /** Custom UI component to display the container for the queried channels, defaults to and accepts same props as: [ChannelListMessenger](https://github.com/GetStream/stream-chat-react/blob/master/src/components/ChannelList/ChannelListMessenger.tsx) */ - List?: React.ComponentType; - /** Custom UI component to display the loading error indicator, defaults to component that renders null */ - LoadingErrorIndicator?: React.ComponentType; - /** Custom UI component to display the loading state, defaults to and accepts same props as: [LoadingChannels](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Loading/LoadingChannels.tsx) */ - LoadingIndicator?: React.ComponentType; /** When true, channels won't dynamically sort by most recent message */ lockChannelOrder?: boolean; /** Function to override the default behavior when a user is added to a channel, corresponds to [notification.added\_to\_channel](https://getstream.io/chat/docs/javascript/event_object/?language=javascript) event */ @@ -140,8 +125,6 @@ export type ChannelListProps = { options?: ChannelOptions; /** Custom UI component to handle channel pagination logic, defaults to and accepts same props as: [LoadMorePaginator](https://github.com/GetStream/stream-chat-react/blob/master/src/components/LoadMore/LoadMorePaginator.tsx) */ Paginator?: React.ComponentType; - /** Custom UI component to display the channel preview in the list, defaults to and accepts same props as: [ChannelPreviewMessenger](https://github.com/GetStream/stream-chat-react/blob/master/src/components/ChannelPreview/ChannelPreviewMessenger.tsx) */ - Preview?: React.ComponentType; /** * Custom interval during which the recovery channel list queries will be prevented. * This is to avoid firing unnecessary queries during internet connection fluctuation. @@ -169,16 +152,12 @@ export type ChannelListProps = { const UnMemoizedChannelList = (props: ChannelListProps) => { const { allowNewMessagesFromUnfilteredChannels = true, - Avatar = DefaultAvatar, channelRenderFilterFn, customActiveChannel, customQueryChannels, EmptyStateIndicator = DefaultEmptyStateIndicator, filters = {}, getLatestMessagePreview, - List = ChannelListMessenger, - LoadingErrorIndicator = NullComponent, - LoadingIndicator = LoadingChannels, lockChannelOrder = false, onAddedToChannel, onChannelDeleted, @@ -191,7 +170,6 @@ const UnMemoizedChannelList = (props: ChannelListProps) => { onRemovedFromChannel, options, Paginator = LoadMorePaginator, - Preview, recoveryThrottleIntervalMs, renderChannels, sendChannelsToList = false, @@ -215,8 +193,12 @@ const UnMemoizedChannelList = (props: ChannelListProps) => { theme, useImageFlagEmojisOnWindows, } = useChatContext('ChannelList'); - const { NotificationList = DefaultNotificationList, Search = DefaultSearch } = - useComponentContext(); // FIXME: use component context to retrieve ChannelListItemUI components too + const { + ChannelListUI = DefaultChannelListUI, + NotificationList = DefaultNotificationList, + Search = DefaultSearch, + } = useComponentContext(); + const channelListRef = useRef(null); const [channelUpdateCount, setChannelUpdateCount] = useState(0); @@ -334,12 +316,10 @@ const UnMemoizedChannelList = (props: ChannelListProps) => { const renderChannel = (item: Channel) => { const previewProps = { activeChannel: channel, - Avatar, channel: item, // forces the update of preview component on channel update channelUpdateCount, getLatestMessagePreview, - Preview, setActiveChannel, watchers, }; @@ -351,7 +331,7 @@ const UnMemoizedChannelList = (props: ChannelListProps) => { const className = clsx( customClasses?.chat ?? 'str-chat', theme, - customClasses?.channelList ?? `${baseClass} ${baseClass}-react`, + customClasses?.channelList ?? `${baseClass}`, { 'str-chat--windows-flags': useImageFlagEmojisOnWindows && navigator.userAgent.match(/Win/), @@ -369,15 +349,13 @@ const UnMemoizedChannelList = (props: ChannelListProps) => { {showChannelSearch && } {showChannelList && ( - {!loadedChannels?.length ? ( @@ -393,7 +371,7 @@ const UnMemoizedChannelList = (props: ChannelListProps) => { : loadedChannels.map((channel) => renderChannel(channel))} )} - + )} diff --git a/src/components/ChannelList/ChannelListMessenger.tsx b/src/components/ChannelList/ChannelListMessenger.tsx deleted file mode 100644 index 7b59daf874..0000000000 --- a/src/components/ChannelList/ChannelListMessenger.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import React from 'react'; -import type { PropsWithChildren } from 'react'; -import type { APIErrorResponse, Channel, ErrorFromResponse } from 'stream-chat'; - -import { LoadingChannels } from '../Loading/LoadingChannels'; -import { NullComponent } from '../UtilityComponents'; -import { useTranslationContext } from '../../context'; -import type { LoadingErrorIndicatorProps } from '../Loading'; - -export type ChannelListMessengerProps = { - /** Whether the channel query request returned an errored response */ - error: ErrorFromResponse | null; - /** The channels currently loaded in the list, only defined if `sendChannelsToList` on `ChannelList` is true */ - loadedChannels?: Channel[]; - /** Whether the channels are currently loading */ - loading?: boolean; - /** Custom UI component to display the loading error indicator, defaults to component that renders null */ - LoadingErrorIndicator?: React.ComponentType; - /** Custom UI component to display a loading indicator, defaults to and accepts same props as: [LoadingChannels](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Loading/LoadingChannels.tsx) */ - LoadingIndicator?: React.ComponentType; - /** Local state hook that resets the currently loaded channels */ - setChannels?: React.Dispatch>; -}; - -/** - * A preview list of channels, allowing you to select the channel you want to open - */ -export const ChannelListMessenger = ( - props: PropsWithChildren, -) => { - const { - children, - error = null, - loading, - LoadingErrorIndicator = NullComponent, - LoadingIndicator = LoadingChannels, - } = props; - const { t } = useTranslationContext('ChannelListMessenger'); - - if (error) { - return ; - } - - if (loading) { - return ( -
-
- -
-
- ); - } - - return ( -
-
- {children} -
-
- ); -}; diff --git a/src/components/ChannelList/ChannelListUI.tsx b/src/components/ChannelList/ChannelListUI.tsx new file mode 100644 index 0000000000..cfc630da34 --- /dev/null +++ b/src/components/ChannelList/ChannelListUI.tsx @@ -0,0 +1,44 @@ +import React from 'react'; +import type { PropsWithChildren } from 'react'; +import type { APIErrorResponse, Channel, ErrorFromResponse } from 'stream-chat'; + +import { LoadingChannels } from '../Loading/LoadingChannels'; +import { NullComponent } from '../UtilityComponents'; +import { useComponentContext, useTranslationContext } from '../../context'; + +export type ChannelListUIProps = { + /** Whether the channel query request returned an errored response */ + error: ErrorFromResponse | null; + /** The channels currently loaded in the list, only defined if `sendChannelsToList` on `ChannelList` is true */ + loadedChannels?: Channel[]; + /** Whether the channels are currently loading */ + loading?: boolean; + /** Local state hook that resets the currently loaded channels */ + setChannels?: React.Dispatch>; +}; + +/** + * A preview list of channels, allowing you to select the channel you want to open + */ +export const ChannelListUI = (props: PropsWithChildren) => { + const { children, error = null, loading = false } = props; + const { LoadingErrorIndicator = NullComponent, LoadingIndicator = LoadingChannels } = + useComponentContext('ChannelListUI'); + const { t } = useTranslationContext('ChannelListUI'); + + if (error) { + return ; + } + + return ( +
+
+ {loading ? : children} +
+
+ ); +}; diff --git a/src/components/ChannelList/__tests__/ChannelList.test.js b/src/components/ChannelList/__tests__/ChannelList.test.js index 84ddbaf9d9..9c74a6cd65 100644 --- a/src/components/ChannelList/__tests__/ChannelList.test.js +++ b/src/components/ChannelList/__tests__/ChannelList.test.js @@ -46,7 +46,7 @@ import { useChatContext, WithComponents, } from '../../../context'; -import { ChannelListMessenger } from '../ChannelListMessenger'; +import { ChannelListUI } from '../ChannelListUI'; expect.extend(toHaveNoViolations); @@ -391,7 +391,7 @@ describe('ChannelList', () => { const ChannelListMessengerPropsInterceptor = (props) => { channelListMessengerLoadingHistory.push(props.loading); - return ; + return ; }; await render( diff --git a/src/components/ChannelList/__tests__/ChannelListMessenger.test.js b/src/components/ChannelList/__tests__/ChannelListUI.test.js similarity index 91% rename from src/components/ChannelList/__tests__/ChannelListMessenger.test.js rename to src/components/ChannelList/__tests__/ChannelListUI.test.js index 7a947b3f02..e8a5891fd8 100644 --- a/src/components/ChannelList/__tests__/ChannelListMessenger.test.js +++ b/src/components/ChannelList/__tests__/ChannelListUI.test.js @@ -2,7 +2,7 @@ import React from 'react'; import { cleanup, render } from '@testing-library/react'; import '@testing-library/jest-dom'; -import { ChannelListMessenger } from '../ChannelListMessenger'; +import { ChannelListUI } from '../ChannelListUI'; import { TranslationProvider } from '../../../context'; import { mockTranslationContext } from '../../../mock-builders'; @@ -12,7 +12,7 @@ console.warn = () => null; const Component = ({ error = false, loading = false }) => ( -
Loading Error Indicator
} @@ -20,7 +20,7 @@ const Component = ({ error = false, loading = false }) => ( >
children 1
children 2
-
+
); diff --git a/src/components/ChannelList/index.ts b/src/components/ChannelList/index.ts index 6407096fa7..2f9989bb1e 100644 --- a/src/components/ChannelList/index.ts +++ b/src/components/ChannelList/index.ts @@ -1,4 +1,4 @@ export * from './ChannelList'; -export * from './ChannelListMessenger'; +export * from './ChannelListUI'; export * from './hooks'; export * from './utils'; diff --git a/src/components/ChannelList/styling/ChannelList.scss b/src/components/ChannelList/styling/ChannelList.scss index 72751173cc..655e19c766 100644 --- a/src/components/ChannelList/styling/ChannelList.scss +++ b/src/components/ChannelList/styling/ChannelList.scss @@ -123,12 +123,12 @@ @include utils.scrollable-y; @include utils.component-layer-overrides('channel-list'); - .str-chat__channel-list-messenger { + .str-chat__channel-list-inner { height: 100%; overflow: hidden; padding-bottom: var(--str-chat__spacing-2_5); - .str-chat__channel-list-messenger__main { + .str-chat__channel-list-inner__main { height: 100%; overflow-y: auto; diff --git a/src/components/ChannelListItem/ChannelListItem.tsx b/src/components/ChannelListItem/ChannelListItem.tsx index 7df5158fca..9ebf33bd66 100644 --- a/src/components/ChannelListItem/ChannelListItem.tsx +++ b/src/components/ChannelListItem/ChannelListItem.tsx @@ -10,7 +10,6 @@ import { getLatestMessagePreview as defaultGetLatestMessagePreview } from './uti import { useTranslationContext } from '../../context/TranslationContext'; import { useMessageDeliveryStatus } from './hooks/useMessageDeliveryStatus'; import type { MessageDeliveryStatus } from './hooks/useMessageDeliveryStatus'; -import type { ChannelAvatarProps } from '../Avatar/ChannelAvatar'; import type { GroupChannelDisplayInfo } from './utils'; import { type ChatContextValue, @@ -47,8 +46,6 @@ export type ChannelListItemProps = { active?: boolean; /** Current selected channel object */ activeChannel?: Channel; - /** UI component to display an avatar, defaults to [Avatar](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Avatar/Avatar.tsx) component and accepts the same props as: [ChannelAvatar](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Avatar/ChannelAvatar.tsx) */ - Avatar?: React.ComponentType; /** Forces the update of preview component on channel update */ channelUpdateCount?: number; /** Custom class for the channel preview root */ diff --git a/src/components/ChannelListItem/ChannelListItemTimestamp.tsx b/src/components/ChannelListItem/ChannelListItemTimestamp.tsx index a0aba65f5b..1de5921482 100644 --- a/src/components/ChannelListItem/ChannelListItemTimestamp.tsx +++ b/src/components/ChannelListItem/ChannelListItemTimestamp.tsx @@ -30,7 +30,10 @@ export function ChannelListItemTimestamp({ lastMessage }: ChannelListItemTimesta if (!when) return null; return ( -