diff --git a/examples/TypeScriptMessaging/App.tsx b/examples/TypeScriptMessaging/App.tsx index 119755c42..608ef8805 100644 --- a/examples/TypeScriptMessaging/App.tsx +++ b/examples/TypeScriptMessaging/App.tsx @@ -12,7 +12,6 @@ import { MessageInput, MessageList, OverlayProvider, - SqliteClient, Streami18n, Thread, ThreadContextValue, @@ -68,11 +67,16 @@ const user = { id: 'luke_skywalker', }; const filters = { - archived: true, + archived: false, members: { $in: ['luke_skywalker'] }, type: 'messaging', }; -const sort: ChannelSort = { last_updated: -1 }; + +const sort: ChannelSort = [ + { pinned_at: 1 }, + { last_message_at: -1 }, + { updated_at: -1 }, +]; /** * Start playing with streami18n instance here: diff --git a/package/src/components/ChannelList/ChannelList.tsx b/package/src/components/ChannelList/ChannelList.tsx index d929d7ac3..1bd08c3e7 100644 --- a/package/src/components/ChannelList/ChannelList.tsx +++ b/package/src/components/ChannelList/ChannelList.tsx @@ -33,7 +33,6 @@ import type { DefaultStreamChatGenerics } from '../../types/types'; import { ChannelPreviewMessenger } from '../ChannelPreview/ChannelPreviewMessenger'; import { EmptyStateIndicator as EmptyStateIndicatorDefault } from '../Indicators/EmptyStateIndicator'; import { LoadingErrorIndicator as LoadingErrorIndicatorDefault } from '../Indicators/LoadingErrorIndicator'; -import { shouldConsiderArchivedChannels } from './hooks/utils'; import { useChannelMemberUpdated } from './hooks/listeners/useMemberUpdated'; export type ChannelListProps< @@ -166,13 +165,14 @@ export type ChannelListProps< * @param setChannels Setter for internal state property - `channels`. It's created from useState() hook. * @param event An [Event object](https://getstream.io/chat/docs/event_object) corresponding to `message.new` event * @param considerArchivedChannels If set to true, archived channels will be considered while updating the list of channels + * @param filters Channel filters * @overrideType Function * */ onNewMessage?: ( lockChannelOrder: boolean, setChannels: React.Dispatch[] | null>>, event: Event, - considerArchivedChannels?: boolean, + filters?: ChannelFilters, ) => void; /** * Override the default listener/handler for event `notification.message_new` @@ -180,13 +180,13 @@ export type ChannelListProps< * * @param setChannels Setter for internal state property - `channels`. It's created from useState() hook. * @param event An [Event object](https://getstream.io/chat/docs/event_object) corresponding to `notification.message_new` event - * + * @param filters Channel filters * @overrideType Function * */ onNewMessageNotification?: ( setChannels: React.Dispatch[] | null>>, event: Event, - considerArchivedChannels?: boolean, + filters?: ChannelFilters, ) => void; /** * Function that overrides default behavior when a user gets removed from a channel @@ -200,6 +200,23 @@ export type ChannelListProps< setChannels: React.Dispatch[] | null>>, event: Event, ) => void; + + /** + * Function that overrides default behavior when a channel member.updated event is triggered + * @param lockChannelOrder If set to true, channels won't dynamically sort by most recent message, defaults to false + * @param setChannels Setter for internal state property - `channels`. It's created from useState() hook. + * @param event An [Event object](https://getstream.io/chat/docs/event_object) corresponding to `member.updated` event + * @param filters Channel filters + * @param sort Channel sort options + * @overrideType Function + */ + onChannelMemberUpdated?: ( + lockChannelOrder: boolean, + setChannels: React.Dispatch[] | null>>, + event: Event, + filters?: ChannelFilters, + sort?: ChannelSort, + ) => void; /** * Object containing channel query options * @see See [Channel query documentation](https://getstream.io/chat/docs/query_channels) for a list of available option fields @@ -248,6 +265,7 @@ export const ChannelList = < onAddedToChannel, onChannelDeleted, onChannelHidden, + onChannelMemberUpdated, onChannelTruncated, onChannelUpdated, onChannelVisible, @@ -290,8 +308,6 @@ export const ChannelList = < sort, }); - const considerArchivedChannels = shouldConsiderArchivedChannels(filters); - // Setup event listeners useAddedToChannelNotification({ onAddedToChannel, @@ -329,13 +345,13 @@ export const ChannelList = < lockChannelOrder, onNewMessage, setChannels, - considerArchivedChannels, + filters, }); useNewMessageNotification({ onNewMessageNotification, setChannels, - considerArchivedChannels, + filters, }); useRemovedFromChannelNotification({ @@ -349,7 +365,11 @@ export const ChannelList = < }); useChannelMemberUpdated({ + lockChannelOrder, setChannels, + onChannelMemberUpdated, + filters, + sort, }); const channelIdsStr = channels?.reduce((acc, channel) => `${acc}${channel.cid}`, ''); diff --git a/package/src/components/ChannelList/hooks/listeners/useMemberUpdated.ts b/package/src/components/ChannelList/hooks/listeners/useMemberUpdated.ts index 023a7cb15..b29fb0746 100644 --- a/package/src/components/ChannelList/hooks/listeners/useMemberUpdated.ts +++ b/package/src/components/ChannelList/hooks/listeners/useMemberUpdated.ts @@ -1,6 +1,6 @@ import { useEffect } from 'react'; -import type { Channel, Event } from 'stream-chat'; +import type { Channel, ChannelFilters, ChannelSort, Event } from 'stream-chat'; import { useChatContext } from '../../../../contexts/chatContext/ChatContext'; @@ -8,25 +8,34 @@ import type { DefaultStreamChatGenerics } from '../../../../types/types'; type Parameters = { + lockChannelOrder: boolean; setChannels: React.Dispatch[] | null>>; onChannelMemberUpdated?: ( + lockChannelOrder: boolean, setChannels: React.Dispatch[] | null>>, event: Event, + filters?: ChannelFilters, + sort?: ChannelSort, ) => void; + filters?: ChannelFilters; + sort?: ChannelSort; }; export const useChannelMemberUpdated = < StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics, >({ + lockChannelOrder, onChannelMemberUpdated, setChannels, + filters, + sort, }: Parameters) => { const { client } = useChatContext(); useEffect(() => { const handleEvent = async (event: Event) => { if (typeof onChannelMemberUpdated === 'function') { - onChannelMemberUpdated(setChannels, event); + onChannelMemberUpdated(lockChannelOrder, setChannels, event, filters, sort); } else { if (!event.member?.user || event.member.user.id !== client.userID || !event.channel_type) { return; @@ -43,6 +52,10 @@ export const useChannelMemberUpdated = < const targetChannelIndex = currentChannels.indexOf(targetChannel); const targetChannelExistsWithinList = targetChannelIndex >= 0; + if (lockChannelOrder) { + return currentChannels; + } + const newChannels = [...currentChannels]; if (targetChannelExistsWithinList) { @@ -50,7 +63,10 @@ export const useChannelMemberUpdated = < } // handle archiving (remove channel) - if (typeof member.archived_at === 'string') { + if ( + typeof member.archived_at === 'string' || + (filters && filters.archived === true && member.archived_at === null) + ) { return newChannels; } return currentChannels; diff --git a/package/src/components/ChannelList/hooks/listeners/useNewMessage.ts b/package/src/components/ChannelList/hooks/listeners/useNewMessage.ts index bad10614e..bf993a642 100644 --- a/package/src/components/ChannelList/hooks/listeners/useNewMessage.ts +++ b/package/src/components/ChannelList/hooks/listeners/useNewMessage.ts @@ -1,6 +1,6 @@ import { useEffect } from 'react'; -import type { Channel, Event } from 'stream-chat'; +import type { Channel, ChannelFilters, Event } from 'stream-chat'; import { useChatContext } from '../../../../contexts/chatContext/ChatContext'; @@ -16,9 +16,9 @@ type Parameters[] | null>>, event: Event, - considerArchivedChannels?: boolean, + filters?: ChannelFilters, ) => void; - considerArchivedChannels?: boolean; + filters?: ChannelFilters; }; export const useNewMessage = < @@ -27,14 +27,14 @@ export const useNewMessage = < lockChannelOrder, onNewMessage, setChannels, - considerArchivedChannels = false, + filters, }: Parameters) => { const { client } = useChatContext(); useEffect(() => { const handleEvent = (event: Event) => { if (typeof onNewMessage === 'function') { - onNewMessage(lockChannelOrder, setChannels, event, considerArchivedChannels); + onNewMessage(lockChannelOrder, setChannels, event, filters); } else { setChannels((channels) => { if (!channels) return channels; @@ -46,6 +46,8 @@ export const useNewMessage = < const isTargetChannelArchived = isChannelArchived(targetChannel); + const considerArchivedChannels = filters && filters.archived === false; + // If channel is archived and we don't want to consider archived channels, return existing list if (isTargetChannelArchived && considerArchivedChannels) { return channels; diff --git a/package/src/components/ChannelList/hooks/listeners/useNewMessageNotification.ts b/package/src/components/ChannelList/hooks/listeners/useNewMessageNotification.ts index 376d42825..d6c464b20 100644 --- a/package/src/components/ChannelList/hooks/listeners/useNewMessageNotification.ts +++ b/package/src/components/ChannelList/hooks/listeners/useNewMessageNotification.ts @@ -2,7 +2,7 @@ import { useEffect } from 'react'; import uniqBy from 'lodash/uniqBy'; -import type { Channel, Event } from 'stream-chat'; +import type { Channel, ChannelFilters, Event } from 'stream-chat'; import { useChatContext } from '../../../../contexts/chatContext/ChatContext'; @@ -16,23 +16,24 @@ type Parameters[] | null>>, event: Event, + filters?: ChannelFilters, ) => void; - considerArchivedChannels?: boolean; + filters?: ChannelFilters; }; export const useNewMessageNotification = < StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics, >({ - considerArchivedChannels = false, onNewMessageNotification, setChannels, + filters, }: Parameters) => { const { client } = useChatContext(); useEffect(() => { const handleEvent = async (event: Event) => { if (typeof onNewMessageNotification === 'function') { - onNewMessageNotification(setChannels, event); + onNewMessageNotification(setChannels, event, filters); } else { if (event.channel?.id && event.channel?.type) { const channel = await getChannel({ @@ -42,6 +43,7 @@ export const useNewMessageNotification = < }); // Handle archived channels + const considerArchivedChannels = filters && filters.archived === false; if (isChannelArchived(channel) && considerArchivedChannels) { return; } diff --git a/package/src/components/ChannelList/hooks/utils/index.ts b/package/src/components/ChannelList/hooks/utils/index.ts index ac96f079e..f1e1205ff 100644 --- a/package/src/components/ChannelList/hooks/utils/index.ts +++ b/package/src/components/ChannelList/hooks/utils/index.ts @@ -1,19 +1,5 @@ import { Channel } from 'stream-chat'; import { DefaultStreamChatGenerics } from '../../../../types/types'; -import { ChannelListProps } from '../../ChannelList'; - -/** - * Returns `true` only if `archived` property is set to `false` within `filters`. - */ -export const shouldConsiderArchivedChannels = < - StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics, ->( - filters: ChannelListProps['filters'], -) => { - if (!filters) return false; - - return !filters.archived; -}; export const isChannelPinned = < StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics,