From 32e4867032414d11d1946f6a33a9d0e5aeebbde8 Mon Sep 17 00:00:00 2001 From: MartinCupela <32706194+MartinCupela@users.noreply.github.com> Date: Fri, 1 Dec 2023 11:55:56 +0100 Subject: [PATCH] fix: revert "feat: expose channels state on chat level (#2161)" (#2184) --- .../components/contexts/chat-context.mdx | 78 --------- .../ChannelList/__tests__/ChannelList.test.js | 148 +++++++++--------- .../ChannelList/hooks/usePaginatedChannels.ts | 7 +- src/components/Chat/Chat.tsx | 4 - src/components/Chat/hooks/useChat.ts | 3 - .../Chat/hooks/useCreateChatContext.ts | 6 - src/context/ChatContext.tsx | 11 +- 7 files changed, 74 insertions(+), 183 deletions(-) diff --git a/docusaurus/docs/React/components/contexts/chat-context.mdx b/docusaurus/docs/React/components/contexts/chat-context.mdx index 906530e50..f0d37d1ba 100644 --- a/docusaurus/docs/React/components/contexts/chat-context.mdx +++ b/docusaurus/docs/React/components/contexts/chat-context.mdx @@ -33,14 +33,6 @@ The currently active channel, which populates the [`Channel`](../core-components | ------- | | Channel | -### channels - -State representing the array of loaded channels. Channels query is executed by default only within the [`ChannelList` component](../core-components/channel-list.mdx) in the SDK. - -| Type | -|-------------| -| `Channel[]` | - ### channelsQueryState Exposes API that: @@ -127,76 +119,6 @@ You can override the default behavior by pulling it from context and then utiliz | -------- | | function | -### setChannels - -Sets the list of `Channel` objects to be rendered by `ChannelList` component. One have to be careful, when to call `setChannels` as the first channels query executed by the `ChannelList` overrides the whole [`channels` state](#channels). In that case it is better to subscribe to `client` event `channels.queried` and only then set the channels. -In the following example, we have a component that sets the active channel based on the id in the URL. It waits until the first channels page is loaded, and then it sets the active channel. If the channel is not present on the first page, it performs additional API request with `getChannel()`: - -```tsx -import {useEffect} from 'react'; -import {useNavigate, useParams} from 'react-router-dom'; -import {ChannelList, getChannel, useChatContext} from 'stream-chat-react'; -import {ChannelFilters, ChannelOptions, ChannelSort, Event} from 'stream-chat'; - -const DEFAULT_CHANNEL = 'general'; -const CHANNEL_TYPE = 'messaging'; - -export const ChannelListWrapper = () => { - const { channelId } = useParams(); - const navigate = useNavigate(); - const { client, channel, setActiveChannel, setChannels } = useChatContext(); - - const filters: ChannelFilters = { type: CHANNEL_TYPE, members: { $in: [client.user?.id || ''] } }; - const options: ChannelOptions = { state: true, presence: true, limit: 10 }; - const sort: ChannelSort = { last_message_at: -1, updated_at: -1 }; - - // set active channel only if URL param changed - useEffect(() => { - if (!channelId) return navigate(`/${DEFAULT_CHANNEL}`); - - if (channel?.id === channelId || !client) return; - - let subscription: { unsubscribe: () => void } | undefined; - if(!channel?.id || channel?.id !== channelId) { - subscription = client.on('channels.queried', (event: Event) => { - // check, whether the channel has already been loaded with the first page - const loadedChannelData = event.queriedChannels?.channels.find((response) => response.channel.id === channelId); - - if (loadedChannelData) { - setActiveChannel(client.channel( CHANNEL_TYPE, channelId)); - subscription?.unsubscribe(); - return; - } - - return getChannel({client, id: channelId, type: CHANNEL_TYPE}).then((newActiveChannel) => { - setActiveChannel(newActiveChannel); - setChannels((channels) => { - return ([newActiveChannel, ...channels.filter((ch) => ch.data?.cid !== newActiveChannel.data?.cid)]); - }); - }); - }); - } - - return () => { - subscription?.unsubscribe(); - }; - }, [channel?.id, channelId, setChannels, client, navigate, setActiveChannel]); - - return ( - - ); -}; -``` - -| Type | -|---------------------------------------| -| `Dispatch>` | - ### theme Deprecated and to be removed in a future major release. Use the `customStyles` prop to adjust CSS variables and [customize the theme](../../guides/theming/css-and-theming.mdx#css-variables) of your app. diff --git a/src/components/ChannelList/__tests__/ChannelList.test.js b/src/components/ChannelList/__tests__/ChannelList.test.js index b9ce92b96..b18f4641d 100644 --- a/src/components/ChannelList/__tests__/ChannelList.test.js +++ b/src/components/ChannelList/__tests__/ChannelList.test.js @@ -48,21 +48,6 @@ const channelsQueryStateMock = { setQueryInProgress: jest.fn(), }; -const ChatContextOverrider = ({ chatContext, children }) => { - const existingContext = useChatContext(); - return ( - - {children} - - ); -}; - /** * We use the following custom UI components for preview and list. * If we use ChannelPreviewMessenger or ChannelPreviewLastMessage here, then changes @@ -131,7 +116,6 @@ describe('ChannelList', () => { client: chatClient, closeMobileNav, navOpen: true, - setChannels: jest.fn(), }} > @@ -163,7 +147,6 @@ describe('ChannelList', () => { client: chatClient, closeMobileNav, navOpen: false, - setChannels: jest.fn(), }} > @@ -409,7 +392,6 @@ describe('ChannelList', () => { describe('Default and custom active channel', () => { let setActiveChannel; - let setChannels; const watchersConfig = { limit: 20, offset: 0 }; const testSetActiveChannelCall = (channelInstance) => waitFor(() => { @@ -420,7 +402,6 @@ describe('ChannelList', () => { beforeEach(() => { setActiveChannel = jest.fn(); - setChannels = jest.fn(); useMockedApis(chatClient, [queryChannelsApi([testChannel1, testChannel2])]); }); @@ -431,7 +412,6 @@ describe('ChannelList', () => { channelsQueryState: channelsQueryStateMock, client: chatClient, setActiveChannel, - setChannels, }} > { channelsQueryState: channelsQueryStateMock, client: chatClient, setActiveChannel, - setChannels, }} > { }); it('should render channel with id `customActiveChannel` at top of the list', async () => { - useMockedApis(chatClient, [getOrCreateChannelApi(testChannel2)]); - jest - .spyOn(chatClient, 'queryChannels') - .mockImplementationOnce(() => - chatClient.hydrateActiveChannels([testChannel1, testChannel2]), - ); - await act(async () => { - await render( - - - , - ); - }); + const { container, getAllByRole, getByRole, getByTestId } = render( + + + , + ); // Wait for list of channels to load in DOM. - await waitFor(() => { - expect(screen.getByRole('list')).toBeInTheDocument(); - const items = screen.getAllByRole('listitem'); + await waitFor(async () => { + expect(getByRole('list')).toBeInTheDocument(); + const items = getAllByRole('listitem'); // Get the closest listitem to the channel that received new message. - const channelPreview = screen - .getByTestId(testChannel2.channel.id) - .closest(ROLE_LIST_ITEM_SELECTOR); + const channelPreview = getByTestId(testChannel2.channel.id).closest( + ROLE_LIST_ITEM_SELECTOR, + ); expect(channelPreview.isEqualNode(items[0])).toBe(true); + const results = await axe(container); + expect(results).toHaveNoViolations(); }); - jest.restoreAllMocks(); }); describe('channel search', () => { @@ -555,16 +535,20 @@ describe('ChannelList', () => { const renderComponents = (chatContext = {}, channeListProps) => render( - - - - - , + + + , ); it.each([['1'], ['2']])( @@ -1209,20 +1193,19 @@ describe('ChannelList', () => { it('should unset activeChannel if it was deleted', async () => { const setActiveChannel = jest.fn(); const { container, getByRole } = render( - - - - - , + + + , ); // Wait for list of channels to load in DOM. @@ -1274,21 +1257,32 @@ describe('ChannelList', () => { }); it('should unset activeChannel if it was hidden', async () => { + const setActiveChannel = jest.fn(); const { container, getByRole } = render( - - - , + + + , ); + // Wait for list of channels to load in DOM. await waitFor(() => { - expect(screen.getByTestId(testChannel1.channel.id)).toBeInTheDocument(); expect(getByRole('list')).toBeInTheDocument(); }); act(() => dispatchChannelHiddenEvent(chatClient, testChannel1.channel)); await waitFor(() => { - expect(screen.queryByTestId(testChannel1.channel.id)).not.toBeInTheDocument(); + expect(setActiveChannel).toHaveBeenCalledTimes(1); }); const results = await axe(container); expect(results).toHaveNoViolations(); diff --git a/src/components/ChannelList/hooks/usePaginatedChannels.ts b/src/components/ChannelList/hooks/usePaginatedChannels.ts index 182e847ec..ec955b11d 100644 --- a/src/components/ChannelList/hooks/usePaginatedChannels.ts +++ b/src/components/ChannelList/hooks/usePaginatedChannels.ts @@ -26,11 +26,9 @@ export const usePaginatedChannels = < recoveryThrottleIntervalMs: number = RECOVER_LOADED_CHANNELS_THROTTLE_INTERVAL_IN_MS, ) => { const { - channels, channelsQueryState: { error, setError, setQueryInProgress }, - setChannels, - } = useChatContext('usePaginatedChannels'); - + } = useChatContext('usePaginatedChannels'); + const [channels, setChannels] = useState>>([]); const [hasNextPage, setHasNextPage] = useState(true); const lastRecoveryTimestamp = useRef(); @@ -117,7 +115,6 @@ export const usePaginatedChannels = < queryChannels('reload'); }, [filterString, sortString]); - // FIXME: state refactor (breaking change) is needed - do not forward `channels` and `setChannel` return { channels, hasNextPage, diff --git a/src/components/Chat/Chat.tsx b/src/components/Chat/Chat.tsx index 729909f64..2e488c5f1 100644 --- a/src/components/Chat/Chat.tsx +++ b/src/components/Chat/Chat.tsx @@ -87,7 +87,6 @@ export const Chat = < const { channel, - channels, closeMobileNav, getAppSettings, latestMessageDatesByChannels, @@ -95,7 +94,6 @@ export const Chat = < navOpen, openMobileNav, setActiveChannel, - setChannels, translators, } = useChat({ client, defaultLanguage, i18nInstance, initialNavOpen }); @@ -112,7 +110,6 @@ export const Chat = < const chatContextValue = useCreateChatContext({ channel, - channels, channelsQueryState, client, closeMobileNav, @@ -123,7 +120,6 @@ export const Chat = < navOpen, openMobileNav, setActiveChannel, - setChannels, theme, themeVersion, useImageFlagEmojisOnWindows, diff --git a/src/components/Chat/hooks/useChat.ts b/src/components/Chat/hooks/useChat.ts index 4ff91f7c4..2f949f2d6 100644 --- a/src/components/Chat/hooks/useChat.ts +++ b/src/components/Chat/hooks/useChat.ts @@ -36,7 +36,6 @@ export const useChat = < userLanguage: 'en', }); - const [channels, setChannels] = useState>>([]); const [channel, setChannel] = useState>(); const [mutes, setMutes] = useState>>([]); const [navOpen, setNavOpen] = useState(initialNavOpen); @@ -124,7 +123,6 @@ export const useChat = < return { channel, - channels, closeMobileNav, getAppSettings, latestMessageDatesByChannels, @@ -132,7 +130,6 @@ export const useChat = < navOpen, openMobileNav, setActiveChannel, - setChannels, translators, }; }; diff --git a/src/components/Chat/hooks/useCreateChatContext.ts b/src/components/Chat/hooks/useCreateChatContext.ts index b7b6366c3..3986ff253 100644 --- a/src/components/Chat/hooks/useCreateChatContext.ts +++ b/src/components/Chat/hooks/useCreateChatContext.ts @@ -10,7 +10,6 @@ export const useCreateChatContext = < ) => { const { channel, - channels, channelsQueryState, client, closeMobileNav, @@ -21,7 +20,6 @@ export const useCreateChatContext = < navOpen, openMobileNav, setActiveChannel, - setChannels, theme, themeVersion, useImageFlagEmojisOnWindows, @@ -39,7 +37,6 @@ export const useCreateChatContext = < const chatContext: ChatContextValue = useMemo( () => ({ channel, - channels, channelsQueryState, client, closeMobileNav, @@ -50,7 +47,6 @@ export const useCreateChatContext = < navOpen, openMobileNav, setActiveChannel, - setChannels, theme, themeVersion, useImageFlagEmojisOnWindows, @@ -59,12 +55,10 @@ export const useCreateChatContext = < channelCid, channelsQueryError, channelsQueryInProgress, - channels, clientValues, getAppSettings, mutedUsersLength, navOpen, - setChannels, ], ); diff --git a/src/context/ChatContext.tsx b/src/context/ChatContext.tsx index 14dfb75bd..cc2ed4e5e 100644 --- a/src/context/ChatContext.tsx +++ b/src/context/ChatContext.tsx @@ -1,4 +1,4 @@ -import React, { Dispatch, PropsWithChildren, SetStateAction, useContext } from 'react'; +import React, { PropsWithChildren, useContext } from 'react'; import type { AppSettingsAPIResponse, Channel, Mute } from 'stream-chat'; @@ -28,11 +28,6 @@ export type ThemeVersion = '1' | '2'; export type ChatContextValue< StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics > = { - /** - * State representing the array of loaded channels. - * Channels query is executed by default only by ChannelList component in the SDK. - */ - channels: Channel[]; /** * Indicates, whether a channels query has been triggered within ChannelList by its channels pagination controller. */ @@ -53,10 +48,6 @@ export type ChatContextValue< watchers?: { limit?: number; offset?: number }, event?: React.BaseSyntheticEvent, ) => void; - /** - * Sets the list of Channel objects to be rendered by ChannelList component. - */ - setChannels: Dispatch[]>>; /** * Allows to opt out of the use of legacy CSS (version "1") and opt into the use of the latest SDK's CSS (version "2"). */