diff --git a/src/components/ChannelList/ChannelList.tsx b/src/components/ChannelList/ChannelList.tsx index b871ee298..8e791146a 100644 --- a/src/components/ChannelList/ChannelList.tsx +++ b/src/components/ChannelList/ChannelList.tsx @@ -5,11 +5,7 @@ import { ChannelListMessenger, ChannelListMessengerProps } from './ChannelListMe import { useConnectionRecoveredListener } from './hooks/useConnectionRecoveredListener'; import { useMobileNavigation } from './hooks/useMobileNavigation'; import { CustomQueryChannelsFn, usePaginatedChannels } from './hooks/usePaginatedChannels'; -import { - MAX_QUERY_CHANNELS_LIMIT, - moveChannelUpwards, - shouldConsiderPinnedChannels, -} from './utils'; +import { MAX_QUERY_CHANNELS_LIMIT, moveChannelUpwards } from './utils'; import { Avatar as DefaultAvatar } from '../Avatar'; import { ChannelPreview, ChannelPreviewUIComponentProps } from '../ChannelPreview/ChannelPreview'; @@ -237,8 +233,7 @@ const UnMemoizedChannelList = () => channels, channelToMove, channelToMoveIndexWithinChannels: targetChannelIndex, - considerPinnedChannels, + sort, }); } @@ -169,10 +169,9 @@ export const useChannelListShapeDefaults = () => return customHandler(setChannels, event); } - const considerArchivedChannels = shouldConsiderArchivedChannels(filters); - const considerPinnedChannels = shouldConsiderPinnedChannels(sort); - - if (!event.channel?.type) return; + if (!event.channel) { + return; + } const channel = await getChannel({ client, @@ -180,18 +179,21 @@ export const useChannelListShapeDefaults = () => type: event.channel.type, }); + const considerArchivedChannels = shouldConsiderArchivedChannels(filters); if (isChannelArchived(channel) && considerArchivedChannels) { return; } - if (!allowNewMessagesFromUnfilteredChannels) return; + if (!allowNewMessagesFromUnfilteredChannels) { + return; + } setChannels((channels) => moveChannelUpwards({ channels, channelToMove: channel, channelToMoveIndexWithinChannels: -1, - considerPinnedChannels, + sort, }), ); }, @@ -249,32 +251,44 @@ export const useChannelListShapeDefaults = () => return; } + const member = event.member; const channelType = event.channel_type; const channelId = event.channel_id; const considerPinnedChannels = shouldConsiderPinnedChannels(sort); + // TODO: extract this and consider single property sort object too + const pinnedAtSort = Array.isArray(sort) ? (sort[0]?.pinned_at ?? null) : null; + setChannels((currentChannels) => { const targetChannel = client.channel(channelType, channelId); // assumes that channel instances are not changing const targetChannelIndex = currentChannels.indexOf(targetChannel); const targetChannelExistsWithinList = targetChannelIndex >= 0; + // handle pinning + if (!considerPinnedChannels || lockChannelOrder) return currentChannels; + const newChannels = [...currentChannels]; if (targetChannelExistsWithinList) { newChannels.splice(targetChannelIndex, 1); } - // handle archiving - if (typeof event.member?.archived_at === 'string') { + // handle archiving (remove channel) + if (typeof member.archived_at === 'string') { return newChannels; } - // handle pinning - if (!considerPinnedChannels || lockChannelOrder) return currentChannels; + let lastPinnedChannelIndex: number | null = null; + + // calculate last pinned channel index only if `pinned_at` sort is set to + // ascending order or if it's in descending order while the pin is being removed, otherwise + // we move to the top (index 0) + if (pinnedAtSort === 1 || (pinnedAtSort === -1 && !member.pinned_at)) { + lastPinnedChannelIndex = findLastPinnedChannelIndex({ channels: newChannels }); + } - const lastPinnedChannelIndex = findLastPinnedChannelIndex({ channels: newChannels }); const newTargetChannelIndex = typeof lastPinnedChannelIndex === 'number' ? lastPinnedChannelIndex + 1 : 0; diff --git a/src/components/ChannelList/utils.ts b/src/components/ChannelList/utils.ts index 62c2669e7..7d533f8d8 100644 --- a/src/components/ChannelList/utils.ts +++ b/src/components/ChannelList/utils.ts @@ -1,5 +1,5 @@ import uniqBy from 'lodash.uniqby'; -import type { Channel, ExtendableGenerics } from 'stream-chat'; +import type { Channel, ChannelSort, ExtendableGenerics } from 'stream-chat'; import type { DefaultStreamChatGenerics } from '../../types/types'; import type { ChannelListProps } from './ChannelList'; @@ -59,27 +59,24 @@ export function findLastPinnedChannelIndex({ type MoveChannelUpwardsParams = { channels: Array>; channelToMove: Channel; + sort: ChannelSort; /** * If the index of the channel within `channels` list which is being moved upwards * (`channelToMove`) is known, you can supply it to skip extra calculation. */ channelToMoveIndexWithinChannels?: number; - /** - * Pinned channels should not move within the list based on recent activity, channels which - * receive messages and are not pinned should move upwards but only under the last pinned channel - * in the list. Property defaults to `false` and should be calculated based on existence of - * the `pinned_at` sort option. - */ - considerPinnedChannels?: boolean; }; +/** + * This function should not be used to move pinned already channels. + */ export const moveChannelUpwards = < SCG extends DefaultStreamChatGenerics = DefaultStreamChatGenerics, >({ channels, channelToMove, channelToMoveIndexWithinChannels, - considerPinnedChannels = false, + sort, }: MoveChannelUpwardsParams) => { // get index of channel to move up const targetChannelIndex = @@ -89,14 +86,12 @@ export const moveChannelUpwards = < const targetChannelExistsWithinList = targetChannelIndex >= 0; const targetChannelAlreadyAtTheTop = targetChannelIndex === 0; - if (targetChannelAlreadyAtTheTop) return channels; + // pinned channels should not move within the list based on recent activity, channels which + // receive messages and are not pinned should move upwards but only under the last pinned channel + // in the list + const considerPinnedChannels = shouldConsiderPinnedChannels(sort); - // as position of pinned channels has to stay unchanged, we need to - // find last pinned channel in the list to move the target channel after - let lastPinnedChannelIndex: number | null = null; - if (considerPinnedChannels) { - lastPinnedChannelIndex = findLastPinnedChannelIndex({ channels }); - } + if (targetChannelAlreadyAtTheTop) return channels; const newChannels = [...channels]; @@ -105,6 +100,13 @@ export const moveChannelUpwards = < newChannels.splice(targetChannelIndex, 1); } + // as position of pinned channels has to stay unchanged, we need to + // find last pinned channel in the list to move the target channel after + let lastPinnedChannelIndex: number | null = null; + if (considerPinnedChannels) { + lastPinnedChannelIndex = findLastPinnedChannelIndex({ channels: newChannels }); + } + // re-insert it at the new place (to specific index if pinned channels are considered) newChannels.splice( typeof lastPinnedChannelIndex === 'number' ? lastPinnedChannelIndex + 1 : 0,