Skip to content

Commit

Permalink
Add archiving handlers
Browse files Browse the repository at this point in the history
  • Loading branch information
arnautov-anton committed Dec 2, 2024
1 parent 207a724 commit f11dd02
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 48 deletions.
7 changes: 3 additions & 4 deletions src/components/ChannelList/ChannelList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ const UnMemoizedChannelList = <SCG extends DefaultStreamChatGenerics = DefaultSt
customActiveChannel,
customQueryChannels,
EmptyStateIndicator = DefaultEmptyStateIndicator,
filters,
filters = {},
getLatestMessagePreview,
LoadingErrorIndicator = NullComponent,
LoadingIndicator = LoadingChannels,
Expand Down Expand Up @@ -284,11 +284,10 @@ const UnMemoizedChannelList = <SCG extends DefaultStreamChatGenerics = DefaultSt

useMobileNavigation(channelListRef, navOpen, closeMobileNav);

const considerPinnedChannels = shouldConsiderPinnedChannels(sort);

const { customFn, defaultFn } = usePrepareShapeHandlers<SCG>({
allowNewMessagesFromUnfilteredChannels,
considerPinnedChannels,
filters,
sort,
lockChannelOrder,
onAddedToChannel,
onChannelDeleted,
Expand Down
111 changes: 74 additions & 37 deletions src/components/ChannelList/hooks/useChannelListShape.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,14 @@ import { Dispatch, SetStateAction, useCallback, useEffect, useMemo, useRef } fro
import { Channel, Event, ExtendableGenerics } from 'stream-chat';
import uniqBy from 'lodash.uniqby';

import { isChannelPinned } from '.';
import { findLastPinnedChannelIndex, moveChannelUpwards } from '../utils';
import {
findLastPinnedChannelIndex,
isChannelArchived,
isChannelPinned,
moveChannelUpwards,
shouldConsiderArchivedChannels,
shouldConsiderPinnedChannels,
} from '../utils';
import { useChatContext } from '../../../context';
import { getChannel } from '../../../utils';
import { ChannelListProps } from '../ChannelList';
Expand All @@ -27,16 +33,14 @@ type RepeatedParameters<SCG extends ExtendableGenerics> = {
type HandleMessageNewParameters<SCG extends ExtendableGenerics> = BaseParameters<SCG> &
RepeatedParameters<SCG> & {
allowNewMessagesFromUnfilteredChannels: boolean;
considerPinnedChannels: boolean;
lockChannelOrder: boolean;
};
} & Required<Pick<ChannelListProps<SCG>, 'filters' | 'sort'>>;

type HandleNotificationMessageNewParameters<SCG extends ExtendableGenerics> = BaseParameters<SCG> &
RepeatedParameters<SCG> & {
allowNewMessagesFromUnfilteredChannels: boolean;
considerPinnedChannels: boolean;
lockChannelOrder: boolean;
};
} & Required<Pick<ChannelListProps<SCG>, 'filters' | 'sort'>>;

type HandleNotificationRemovedFromChannelParameters<SCG extends ExtendableGenerics> =
BaseParameters<SCG> & RepeatedParameters<SCG>;
Expand All @@ -45,14 +49,12 @@ type HandleNotificationAddedToChannelParameters<SCG extends ExtendableGenerics>
BaseParameters<SCG> &
RepeatedParameters<SCG> & {
allowNewMessagesFromUnfilteredChannels: boolean;
considerPinnedChannels: boolean;
lockChannelOrder: boolean;
};
} & Required<Pick<ChannelListProps<SCG>, 'sort'>>;

type HandleMemberUpdatedParameters<SCG extends ExtendableGenerics> = BaseParameters<SCG> & {
considerPinnedChannels: boolean;
lockChannelOrder: boolean;
};
} & Required<Pick<ChannelListProps<SCG>, 'sort'>>;

type HandleChannelDeletedParameters<SCG extends ExtendableGenerics> = BaseParameters<SCG> &
RepeatedParameters<SCG>;
Expand Down Expand Up @@ -97,7 +99,8 @@ export const useChannelListShapeDefaults = <SCG extends ExtendableGenerics>() =>
const handleMessageNew = useCallback(
({
allowNewMessagesFromUnfilteredChannels,
considerPinnedChannels,
filters,
sort,
customHandler,
event,
lockChannelOrder,
Expand All @@ -110,12 +113,17 @@ export const useChannelListShapeDefaults = <SCG extends ExtendableGenerics>() =>
setChannels((channels) => {
const targetChannelIndex = channels.findIndex((channel) => channel.cid === event.cid);
const targetChannelExistsWithinList = targetChannelIndex >= 0;
const targetChannel = channels[targetChannelIndex];

const isTargetChannelPinned = isChannelPinned({
channel: channels[targetChannelIndex],
});
const isTargetChannelPinned = isChannelPinned(targetChannel);
const isTargetChannelArchived = isChannelArchived(targetChannel);

const considerArchivedChannels = shouldConsiderArchivedChannels(filters);
const considerPinnedChannels = shouldConsiderPinnedChannels(sort);

if (
// target channel is archived
(isTargetChannelArchived && considerArchivedChannels) ||
// target channel is pinned
(isTargetChannelPinned && considerPinnedChannels) ||
// list order is locked
Expand Down Expand Up @@ -151,6 +159,8 @@ export const useChannelListShapeDefaults = <SCG extends ExtendableGenerics>() =>
const handleNotificationMessageNew = useCallback(
async ({
allowNewMessagesFromUnfilteredChannels,
sort,
filters,
customHandler,
event,
setChannels,
Expand All @@ -159,14 +169,31 @@ export const useChannelListShapeDefaults = <SCG extends ExtendableGenerics>() =>
return customHandler(setChannels, event);
}

if (allowNewMessagesFromUnfilteredChannels && event.channel?.type) {
const channel = await getChannel({
client,
id: event.channel.id,
type: event.channel.type,
});
setChannels((channels) => uniqBy([channel, ...channels], 'cid'));
const considerArchivedChannels = shouldConsiderArchivedChannels(filters);
const considerPinnedChannels = shouldConsiderPinnedChannels(sort);

if (!event.channel?.type) return;

const channel = await getChannel({
client,
id: event.channel.id,
type: event.channel.type,
});

if (isChannelArchived(channel) && considerArchivedChannels) {
return;
}

if (!allowNewMessagesFromUnfilteredChannels) return;

setChannels((channels) =>
moveChannelUpwards({
channels,
channelToMove: channel,
channelToMoveIndexWithinChannels: -1,
considerPinnedChannels,
}),
);
},
[client],
);
Expand Down Expand Up @@ -217,19 +244,16 @@ export const useChannelListShapeDefaults = <SCG extends ExtendableGenerics>() =>
);

const handleMemberUpdated = useCallback(
({
considerPinnedChannels,
event,
lockChannelOrder,
setChannels,
}: HandleMemberUpdatedParameters<SCG>) => {
if (lockChannelOrder || !considerPinnedChannels) return;
({ sort, event, lockChannelOrder, setChannels }: HandleMemberUpdatedParameters<SCG>) => {
if (!event.member?.user || event.member.user.id !== client.userID || !event.channel_type) {
return;
}

if (!event.member || !event.channel_type) return;
// const member = e.member;
const channelType = event.channel_type;
const channelId = event.channel_id;

const considerPinnedChannels = shouldConsiderPinnedChannels(sort);

setChannels((currentChannels) => {
const targetChannel = client.channel(channelType, channelId);
// assumes that channel instances are not changing
Expand All @@ -242,6 +266,14 @@ export const useChannelListShapeDefaults = <SCG extends ExtendableGenerics>() =>
newChannels.splice(targetChannelIndex, 1);
}

// handle archiving
if (typeof event.member?.archived_at === 'string') {
return newChannels;
}

// handle pinning
if (!considerPinnedChannels || lockChannelOrder) return currentChannels;

const lastPinnedChannelIndex = findLastPinnedChannelIndex({ channels: newChannels });
const newTargetChannelIndex =
typeof lastPinnedChannelIndex === 'number' ? lastPinnedChannelIndex + 1 : 0;
Expand Down Expand Up @@ -385,7 +417,10 @@ export const useChannelListShapeDefaults = <SCG extends ExtendableGenerics>() =>
};

type UseDefaultHandleChannelListShapeParameters<SCG extends ExtendableGenerics> = Required<
Pick<ChannelListProps<SCG>, 'allowNewMessagesFromUnfilteredChannels' | 'lockChannelOrder'>
Pick<
ChannelListProps<SCG>,
'allowNewMessagesFromUnfilteredChannels' | 'lockChannelOrder' | 'filters' | 'sort'
>
> &
Pick<
ChannelListProps<SCG>,
Expand All @@ -399,7 +434,6 @@ type UseDefaultHandleChannelListShapeParameters<SCG extends ExtendableGenerics>
| 'onMessageNewHandler'
| 'onRemovedFromChannel'
> & {
considerPinnedChannels: boolean;
customHandleChannelListShape?: (data: {
defaults: ReturnType<typeof useChannelListShapeDefaults<SCG>>;
event: Event<SCG>;
Expand All @@ -410,7 +444,6 @@ type UseDefaultHandleChannelListShapeParameters<SCG extends ExtendableGenerics>

export const usePrepareShapeHandlers = <SCG extends ExtendableGenerics>({
allowNewMessagesFromUnfilteredChannels,
considerPinnedChannels,
customHandleChannelListShape,
lockChannelOrder,
onAddedToChannel,
Expand All @@ -423,6 +456,8 @@ export const usePrepareShapeHandlers = <SCG extends ExtendableGenerics>({
onMessageNewHandler,
onRemovedFromChannel,
setChannels,
filters,
sort,
}: UseDefaultHandleChannelListShapeParameters<SCG>) => {
const defaults = useChannelListShapeDefaults<SCG>();

Expand All @@ -439,7 +474,8 @@ export const usePrepareShapeHandlers = <SCG extends ExtendableGenerics>({
case 'message.new':
defaults.handleMessageNew({
allowNewMessagesFromUnfilteredChannels,
considerPinnedChannels,
sort,
filters,
customHandler: onMessageNewHandler,
event,
lockChannelOrder,
Expand All @@ -449,7 +485,8 @@ export const usePrepareShapeHandlers = <SCG extends ExtendableGenerics>({
case 'notification.message_new':
defaults.handleNotificationMessageNew({
allowNewMessagesFromUnfilteredChannels,
considerPinnedChannels,
sort,
filters,
customHandler: onMessageNew,
event,
lockChannelOrder,
Expand All @@ -459,7 +496,7 @@ export const usePrepareShapeHandlers = <SCG extends ExtendableGenerics>({
case 'notification.added_to_channel':
defaults.handleNotificationAddedToChannel({
allowNewMessagesFromUnfilteredChannels,
considerPinnedChannels,
sort,
customHandler: onAddedToChannel,
event,
lockChannelOrder,
Expand Down Expand Up @@ -497,7 +534,7 @@ export const usePrepareShapeHandlers = <SCG extends ExtendableGenerics>({
break;
case 'member.updated':
defaults.handleMemberUpdated({
considerPinnedChannels,
sort,
event,
lockChannelOrder,
setChannels,
Expand Down
44 changes: 37 additions & 7 deletions src/components/ChannelList/utils.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import uniqBy from 'lodash.uniqby';
import type { Channel, ExtendableGenerics } from 'stream-chat';

import { isChannelPinned } from './hooks';
import type { DefaultStreamChatGenerics } from '../../types/types';
import { ChannelListProps } from './ChannelList';
import type { ChannelListProps } from './ChannelList';

export const MAX_QUERY_CHANNELS_LIMIT = 30;

Expand Down Expand Up @@ -45,7 +44,7 @@ export function findLastPinnedChannelIndex<SCG extends ExtendableGenerics>({
let lastPinnedChannelIndex: number | null = null;

for (const channel of channels) {
if (!isChannelPinned({ channel })) break;
if (!isChannelPinned(channel)) break;

if (typeof lastPinnedChannelIndex === 'number') {
lastPinnedChannelIndex++;
Expand Down Expand Up @@ -75,7 +74,7 @@ type MoveChannelUpwardsParams<SCG extends DefaultStreamChatGenerics = DefaultStr
};

export const moveChannelUpwards = <
SCG extends DefaultStreamChatGenerics = DefaultStreamChatGenerics
SCG extends DefaultStreamChatGenerics = DefaultStreamChatGenerics,
>({
channels,
channelToMove,
Expand Down Expand Up @@ -117,14 +116,45 @@ export const moveChannelUpwards = <
};

/**
* Set to true only if `{ pinned_at: -1 }` option is first within the `sort` array.
* Returns true only if `{ pinned_at: -1 }` or `{ pinned_at: 1 }` option is first within the `sort` array.
*/
export const shouldConsiderPinnedChannels = (sort: ChannelListProps['sort']) => {
export const shouldConsiderPinnedChannels = <SCG extends ExtendableGenerics>(
sort: ChannelListProps<SCG>['sort'],
) => {
if (!sort) return false;

if (!Array.isArray(sort)) return false;

const [option] = sort;

return option?.pinned_at === -1;
if (!option?.pinned_at) return false;

return Math.abs(option.pinned_at) === 1;
};

/**
* Returns `true` only if `archived` property is set to `false` within `filters`.
*/
export const shouldConsiderArchivedChannels = <SCG extends ExtendableGenerics>(
filters: ChannelListProps<SCG>['filters'],
) => {
if (!filters) return false;

return !filters.archived;
};

export const isChannelPinned = <SCG extends ExtendableGenerics>(channel: Channel<SCG>) => {
if (!channel) return false;

const member = channel.state.membership;

return !!member?.pinned_at;
};

export const isChannelArchived = <SCG extends ExtendableGenerics>(channel: Channel<SCG>) => {
if (!channel) return false;

const member = channel.state.membership;

return !!member?.archived_at;
};

0 comments on commit f11dd02

Please sign in to comment.