import React, { useState } from 'react'; import { ChannelListHeaderErrorIndicator } from './ChannelListHeaderErrorIndicator'; import { ChannelListFooterLoadingIndicator } from './ChannelListFooterLoadingIndicator'; import { ChannelListHeaderNetworkDownIndicator } from './ChannelListHeaderNetworkDownIndicator'; import { ChannelListLoadingIndicator } from './ChannelListLoadingIndicator'; import { ChannelListMessenger, ChannelListMessengerProps } from './ChannelListMessenger'; import { useAddedToChannelNotification } from './hooks/listeners/useAddedToChannelNotification'; import { useChannelDeleted } from './hooks/listeners/useChannelDeleted'; import { useChannelHidden } from './hooks/listeners/useChannelHidden'; import { useChannelTruncated } from './hooks/listeners/useChannelTruncated'; import { useChannelUpdated } from './hooks/listeners/useChannelUpdated'; import { useChannelVisible } from './hooks/listeners/useChannelVisible'; import { useConnectionRecovered } from './hooks/listeners/useConnectionRecovered'; import { useNewMessage } from './hooks/listeners/useNewMessage'; import { useNewMessageNotification } from './hooks/listeners/useNewMessageNotification'; import { useRemovedFromChannelNotification } from './hooks/listeners/useRemovedFromChannelNotification'; import { useUserPresence } from './hooks/listeners/useUserPresence'; import { useCreateChannelsContext } from './hooks/useCreateChannelsContext'; import { usePaginatedChannels } from './hooks/usePaginatedChannels'; import { Skeleton as SkeletonDefault } from './Skeleton'; import { ChannelPreviewMessenger } from '../ChannelPreview/ChannelPreviewMessenger'; import { EmptyStateIndicator as EmptyStateIndicatorDefault } from '../Indicators/EmptyStateIndicator'; import { LoadingErrorIndicator as LoadingErrorIndicatorDefault } from '../Indicators/LoadingErrorIndicator'; import { ChannelsContextValue, ChannelsProvider, } from '../../contexts/channelsContext/ChannelsContext'; import type { FlatList } from 'react-native-gesture-handler'; import type { Channel, ChannelFilters, ChannelOptions, ChannelSort, Event } from 'stream-chat'; import type { DefaultAttachmentType, DefaultChannelType, DefaultCommandType, DefaultEventType, DefaultMessageType, DefaultReactionType, DefaultUserType, UnknownType, } from '../../types/types'; export type ChannelListProps< At extends UnknownType = DefaultAttachmentType, Ch extends UnknownType = DefaultChannelType, Co extends string = DefaultCommandType, Ev extends UnknownType = DefaultEventType, Me extends UnknownType = DefaultMessageType, Re extends UnknownType = DefaultReactionType, Us extends UnknownType = DefaultUserType, > = Partial< Pick< ChannelsContextValue, | 'additionalFlatListProps' | 'EmptyStateIndicator' | 'FooterLoadingIndicator' | 'HeaderErrorIndicator' | 'HeaderNetworkDownIndicator' | 'LoadingErrorIndicator' | 'LoadingIndicator' | 'Preview' | 'setFlatListRef' | 'ListHeaderComponent' | 'onSelect' | 'PreviewAvatar' | 'PreviewMessage' | 'PreviewStatus' | 'PreviewTitle' | 'PreviewUnreadCount' | 'loadMoreThreshold' | 'Skeleton' | 'maxUnreadCount' | 'numberOfSkeletons' > > & { /** * Object containing channel query filters * * @see See [Channel query documentation](https://getstream.io/chat/docs/query_channels) for a list of available filter fields * * @overrideType object * */ filters?: ChannelFilters; /** * Custom UI component to display the list of channels * * Default: [ChannelListMessenger](https://getstream.github.io/stream-chat-react-native/v3/#channellistmessenger) */ List?: React.ComponentType>; /** * If set to true, channels won't dynamically sort by most recent message, defaults to false */ lockChannelOrder?: boolean; /** * Function that overrides default behavior when a user gets added to a channel * * @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.added_to_channel` event * * @overrideType Function * */ onAddedToChannel?: ( setChannels: React.Dispatch[]>>, event: Event, ) => void; /** * Function that overrides default behavior when a channel gets deleted. In absence of this prop, the channel will be removed from the list. * * @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 `channel.deleted` event * * @overrideType Function * */ onChannelDeleted?: ( setChannels: React.Dispatch[]>>, event: Event, ) => void; /** * Function that overrides default behavior when a channel gets hidden. In absence of this prop, the channel will be removed from the list. * * @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 `channel.hidden` event * * @overrideType Function * */ onChannelHidden?: ( setChannels: React.Dispatch[]>>, event: Event, ) => void; /** * Function to customize behavior when a channel gets truncated * * @param setChannels Setter for internal state property - `channels`. It's created from useState() hook. * @param event [Event object](https://getstream.io/chat/docs/event_object) corresponding to `channel.truncated` event * * @overrideType Function * */ onChannelTruncated?: ( setChannels: React.Dispatch[]>>, event: Event, ) => void; /** * Function that overrides default behavior when a channel gets updated * * @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 `channel.updated` event * * @overrideType Function * */ onChannelUpdated?: ( setChannels: React.Dispatch[]>>, event: Event, ) => void; /** * Function that overrides default behavior when a channel gets visible. In absence of this prop, the channel will be added to the list. * * @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 `channel.visible` event * * @overrideType Function * */ onChannelVisible?: ( setChannels: React.Dispatch[]>>, event: Event, ) => 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 * * @overrideType Function * */ onMessageNew?: ( setChannels: React.Dispatch[]>>, event: Event, ) => void; /** * Function that overrides default behavior when a user gets removed from a channel * * @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.removed_from_channel` event * * @overrideType Function * */ onRemovedFromChannel?: ( setChannels: React.Dispatch[]>>, event: Event, ) => 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 * */ options?: ChannelOptions; /** * Object containing channel sort parameters * @see See [Channel query documentation](https://getstream.io/chat/docs/query_channels) for a list of available sorting fields * */ sort?: ChannelSort; }; const DEFAULT_FILTERS = {}; const DEFAULT_OPTIONS = {}; const DEFAULT_SORT = {}; /** * This component fetches a list of channels, allowing you to select the channel you want to open. * The ChannelList doesn't provide any UI for the underlying React Native FlatList. UI is determined by the `List` component which is * provided to the ChannelList component as a prop. By default, the ChannelListMessenger component is used as the list UI. * * @example ./ChannelList.md */ export const ChannelList = < At extends UnknownType = DefaultAttachmentType, Ch extends UnknownType = DefaultChannelType, Co extends string = DefaultCommandType, Ev extends UnknownType = DefaultEventType, Me extends UnknownType = DefaultMessageType, Re extends UnknownType = DefaultReactionType, Us extends UnknownType = DefaultUserType, >( props: ChannelListProps, ) => { const { additionalFlatListProps = {}, EmptyStateIndicator = EmptyStateIndicatorDefault, FooterLoadingIndicator = ChannelListFooterLoadingIndicator, filters = DEFAULT_FILTERS, HeaderErrorIndicator = ChannelListHeaderErrorIndicator, HeaderNetworkDownIndicator = ChannelListHeaderNetworkDownIndicator, List = ChannelListMessenger, ListHeaderComponent, LoadingErrorIndicator = LoadingErrorIndicatorDefault, LoadingIndicator = ChannelListLoadingIndicator, // https://github.com/facebook/react-native/blob/a7a7970e543959e9db5281914d5f132beb01db8d/Libraries/Lists/VirtualizedList.js#L466 loadMoreThreshold = 2, lockChannelOrder = false, maxUnreadCount = 255, numberOfSkeletons = 6, onAddedToChannel, onChannelDeleted, onChannelHidden, onChannelVisible, onChannelTruncated, onChannelUpdated, onMessageNew, onRemovedFromChannel, onSelect, options = DEFAULT_OPTIONS, Preview = ChannelPreviewMessenger, PreviewAvatar, PreviewMessage, PreviewStatus, PreviewTitle, PreviewUnreadCount, setFlatListRef, Skeleton = SkeletonDefault, sort = DEFAULT_SORT, } = props; const [forceUpdate, setForceUpdate] = useState(0); const { channels, error, hasNextPage, loadingChannels, loadingNextPage, loadNextPage, refreshing, refreshList, reloadList, setChannels, } = usePaginatedChannels({ filters, options, sort, }); // Setup event listeners useAddedToChannelNotification({ onAddedToChannel, setChannels, }); useChannelDeleted({ onChannelDeleted, setChannels, }); useChannelHidden({ onChannelHidden, setChannels, }); useChannelTruncated({ onChannelTruncated, refreshList, setChannels, setForceUpdate, }); useChannelUpdated({ onChannelUpdated, setChannels, }); useChannelVisible({ onChannelVisible, setChannels, }); useConnectionRecovered({ refreshList, setForceUpdate, }); useNewMessage({ lockChannelOrder, setChannels, }); useNewMessageNotification({ onMessageNew, setChannels, }); useRemovedFromChannelNotification({ onRemovedFromChannel, setChannels, }); useUserPresence({ setChannels, }); const channelsContext = useCreateChannelsContext({ additionalFlatListProps, channels, EmptyStateIndicator, error, FooterLoadingIndicator, forceUpdate, hasNextPage, HeaderErrorIndicator, HeaderNetworkDownIndicator, ListHeaderComponent, loadingChannels, LoadingErrorIndicator, LoadingIndicator, loadingNextPage, loadMoreThreshold, loadNextPage, maxUnreadCount, numberOfSkeletons, onSelect, Preview, PreviewAvatar, PreviewMessage, PreviewStatus, PreviewTitle, PreviewUnreadCount, refreshing, refreshList, reloadList, setFlatListRef: (ref: FlatList> | null) => { if (setFlatListRef) { setFlatListRef(ref); } }, Skeleton, }); return ( /> ); };