Skip to content

Commit

Permalink
feat: channel archiving
Browse files Browse the repository at this point in the history
  • Loading branch information
khushal87 committed Dec 25, 2024
1 parent 4fadc2e commit 4797266
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 37 deletions.
10 changes: 7 additions & 3 deletions examples/TypeScriptMessaging/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import {
MessageInput,
MessageList,
OverlayProvider,
SqliteClient,
Streami18n,
Thread,
ThreadContextValue,
Expand Down Expand Up @@ -68,11 +67,16 @@ const user = {
id: 'luke_skywalker',
};
const filters = {
archived: true,
archived: false,
members: { $in: ['luke_skywalker'] },
type: 'messaging',
};
const sort: ChannelSort<StreamChatGenerics> = { last_updated: -1 };

const sort: ChannelSort<StreamChatGenerics> = [
{ pinned_at: 1 },
{ last_message_at: -1 },
{ updated_at: -1 },
];

/**
* Start playing with streami18n instance here:
Expand Down
36 changes: 28 additions & 8 deletions package/src/components/ChannelList/ChannelList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<
Expand Down Expand Up @@ -166,27 +165,28 @@ 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<React.SetStateAction<Channel<StreamChatGenerics>[] | null>>,
event: Event<StreamChatGenerics>,
considerArchivedChannels?: boolean,
filters?: ChannelFilters<StreamChatGenerics>,
) => void;
/**
* Override the default listener/handler for event `notification.message_new`
* This event is received on channel, which is not being watched.
*
* @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<React.SetStateAction<Channel<StreamChatGenerics>[] | null>>,
event: Event<StreamChatGenerics>,
considerArchivedChannels?: boolean,
filters?: ChannelFilters<StreamChatGenerics>,
) => void;
/**
* Function that overrides default behavior when a user gets removed from a channel
Expand All @@ -200,6 +200,23 @@ export type ChannelListProps<
setChannels: React.Dispatch<React.SetStateAction<Channel<StreamChatGenerics>[] | null>>,
event: Event<StreamChatGenerics>,
) => 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<React.SetStateAction<Channel<StreamChatGenerics>[] | null>>,
event: Event<StreamChatGenerics>,
filters?: ChannelFilters<StreamChatGenerics>,
sort?: ChannelSort<StreamChatGenerics>,
) => 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
Expand Down Expand Up @@ -248,6 +265,7 @@ export const ChannelList = <
onAddedToChannel,
onChannelDeleted,
onChannelHidden,
onChannelMemberUpdated,
onChannelTruncated,
onChannelUpdated,
onChannelVisible,
Expand Down Expand Up @@ -290,8 +308,6 @@ export const ChannelList = <
sort,
});

const considerArchivedChannels = shouldConsiderArchivedChannels(filters);

// Setup event listeners
useAddedToChannelNotification({
onAddedToChannel,
Expand Down Expand Up @@ -329,13 +345,13 @@ export const ChannelList = <
lockChannelOrder,
onNewMessage,
setChannels,
considerArchivedChannels,
filters,
});

useNewMessageNotification({
onNewMessageNotification,
setChannels,
considerArchivedChannels,
filters,
});

useRemovedFromChannelNotification({
Expand All @@ -349,7 +365,11 @@ export const ChannelList = <
});

useChannelMemberUpdated({
lockChannelOrder,
setChannels,
onChannelMemberUpdated,
filters,
sort,
});

const channelIdsStr = channels?.reduce((acc, channel) => `${acc}${channel.cid}`, '');
Expand Down
Original file line number Diff line number Diff line change
@@ -1,32 +1,41 @@
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';

import type { DefaultStreamChatGenerics } from '../../../../types/types';

type Parameters<StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics> =
{
lockChannelOrder: boolean;
setChannels: React.Dispatch<React.SetStateAction<Channel<StreamChatGenerics>[] | null>>;
onChannelMemberUpdated?: (
lockChannelOrder: boolean,
setChannels: React.Dispatch<React.SetStateAction<Channel<StreamChatGenerics>[] | null>>,
event: Event<StreamChatGenerics>,
filters?: ChannelFilters<StreamChatGenerics>,
sort?: ChannelSort<StreamChatGenerics>,
) => void;
filters?: ChannelFilters<StreamChatGenerics>;
sort?: ChannelSort<StreamChatGenerics>;
};

export const useChannelMemberUpdated = <
StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics,
>({
lockChannelOrder,
onChannelMemberUpdated,
setChannels,
filters,
sort,
}: Parameters<StreamChatGenerics>) => {
const { client } = useChatContext<StreamChatGenerics>();

useEffect(() => {
const handleEvent = async (event: Event<StreamChatGenerics>) => {
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;
Expand All @@ -43,14 +52,21 @@ export const useChannelMemberUpdated = <
const targetChannelIndex = currentChannels.indexOf(targetChannel);
const targetChannelExistsWithinList = targetChannelIndex >= 0;

if (lockChannelOrder) {
return currentChannels;
}

const newChannels = [...currentChannels];

if (targetChannelExistsWithinList) {
newChannels.splice(targetChannelIndex, 1);
}

// 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;
Expand Down
Original file line number Diff line number Diff line change
@@ -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';

Expand All @@ -16,9 +16,9 @@ type Parameters<StreamChatGenerics extends DefaultStreamChatGenerics = DefaultSt
lockChannelOrder: boolean,
setChannels: React.Dispatch<React.SetStateAction<Channel<StreamChatGenerics>[] | null>>,
event: Event<StreamChatGenerics>,
considerArchivedChannels?: boolean,
filters?: ChannelFilters<StreamChatGenerics>,
) => void;
considerArchivedChannels?: boolean;
filters?: ChannelFilters<StreamChatGenerics>;
};

export const useNewMessage = <
Expand All @@ -27,14 +27,14 @@ export const useNewMessage = <
lockChannelOrder,
onNewMessage,
setChannels,
considerArchivedChannels = false,
filters,
}: Parameters<StreamChatGenerics>) => {
const { client } = useChatContext<StreamChatGenerics>();

useEffect(() => {
const handleEvent = (event: Event<StreamChatGenerics>) => {
if (typeof onNewMessage === 'function') {
onNewMessage(lockChannelOrder, setChannels, event, considerArchivedChannels);
onNewMessage(lockChannelOrder, setChannels, event, filters);
} else {
setChannels((channels) => {
if (!channels) return channels;
Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -16,23 +16,24 @@ type Parameters<StreamChatGenerics extends DefaultStreamChatGenerics = DefaultSt
onNewMessageNotification?: (
setChannels: React.Dispatch<React.SetStateAction<Channel<StreamChatGenerics>[] | null>>,
event: Event<StreamChatGenerics>,
filters?: ChannelFilters<StreamChatGenerics>,
) => void;
considerArchivedChannels?: boolean;
filters?: ChannelFilters<StreamChatGenerics>;
};

export const useNewMessageNotification = <
StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics,
>({
considerArchivedChannels = false,
onNewMessageNotification,
setChannels,
filters,
}: Parameters<StreamChatGenerics>) => {
const { client } = useChatContext<StreamChatGenerics>();

useEffect(() => {
const handleEvent = async (event: Event<StreamChatGenerics>) => {
if (typeof onNewMessageNotification === 'function') {
onNewMessageNotification(setChannels, event);
onNewMessageNotification(setChannels, event, filters);
} else {
if (event.channel?.id && event.channel?.type) {
const channel = await getChannel({
Expand All @@ -42,6 +43,7 @@ export const useNewMessageNotification = <
});

// Handle archived channels
const considerArchivedChannels = filters && filters.archived === false;
if (isChannelArchived(channel) && considerArchivedChannels) {
return;
}
Expand Down
14 changes: 0 additions & 14 deletions package/src/components/ChannelList/hooks/utils/index.ts
Original file line number Diff line number Diff line change
@@ -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<StreamChatGenerics>['filters'],
) => {
if (!filters) return false;

return !filters.archived;
};

export const isChannelPinned = <
StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics,
Expand Down

0 comments on commit 4797266

Please sign in to comment.