Skip to content

Commit

Permalink
feat: add initializeOnMount prop to ChannelProps
Browse files Browse the repository at this point in the history
  • Loading branch information
MartinCupela committed Oct 5, 2023
1 parent 38796ac commit abc8ca6
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 10 deletions.
59 changes: 54 additions & 5 deletions docusaurus/docs/React/components/core-components/channel.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,14 @@ The Giphy version to render - check the keys of the [Image Object](https://devel
| ------ | -------------- |
| string | 'fixed_height' |

### HeaderComponent

Custom UI component to render at the top of the `MessageList`.

| Type | Default |
| --------- | ------- |
| component | none |

### imageAttachmentSizeHandler

A custom function to provide size configuration for image attachments
Expand All @@ -337,13 +345,54 @@ A custom function to provide size configuration for image attachments
| ---------------------------------------------------------------- |
| `(a: Attachment, e: HTMLElement) => ImageAttachmentConfigration` |

### HeaderComponent
### initializeOnMount

Allows to prevent triggering the `channel.watch()` (triggers channel query HTTP request) call when mounting the `Channel` component (the default behavior) with uninitialized (`channel.initialized`) `Channel` instance. That means that no channel data from the back-end will be received neither channel WS events will be delivered to the client. Preventing to initialize the channel on mount allows us to postpone the channel creation in the Stream's DB to a later point in time, for example, when a first message is sent:

```typescript jsx
import {useCallback} from "react";
import {
getChannel,
MessageInput as StreamMessageInput,
MessageInputProps, MessageToSend,
useChannelActionContext,
useChatContext
} from "stream-chat-react";
import {Message, SendMessageOptions} from "stream-chat";

import {useChannelInitContext} from "../../context/ChannelInitProvider";
import type { MyStreamChatGenerics } from "../../types";

export const MessageInput = (props: MessageInputProps) => {
const {client} = useChatContext();
const {sendMessage} = useChannelActionContext();
const { setInitializedChannelOnMount} = useChannelInitContext();

const submitHandler: MessageInputProps['overrideSubmitHandler'] = useCallback(async (
message: MessageToSend<MyStreamChatGenerics>,
channelCid: string,
customMessageData?: Partial<Message<MyStreamChatGenerics>>,
options?: SendMessageOptions,
) => {
const [channelType, channelId] = channelCid.split(":");
const channel = client.channel(channelType, channelId);
if (!channel.initialized) {
await getChannel({channel, client});
setInitializedChannelOnMount(true);
}

await sendMessage(message, customMessageData, options);
}, [client, sendMessage, setInitializedChannelOnMount]);

Custom UI component to render at the top of the `MessageList`.
return (
<StreamMessageInput {...props} overrideSubmitHandler={submitHandler} />
);
};
```

| Type | Default |
| --------- | ------- |
| component | none |
| Type | Default |
|---------|---------|
| boolean | true |

### Input

Expand Down
14 changes: 11 additions & 3 deletions src/components/Channel/Channel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,8 @@ export type ChannelProps<
EmptyPlaceholder?: React.ReactElement;
/** Custom UI component to be displayed when the `MessageList` is empty, defaults to and accepts same props as: [EmptyStateIndicator](https://github.com/GetStream/stream-chat-react/blob/master/src/components/EmptyStateIndicator/EmptyStateIndicator.tsx) */
EmptyStateIndicator?: ComponentContextValue<StreamChatGenerics>['EmptyStateIndicator'];
/** A global flag to toggle the URL enrichment and link previews in `MessageInput` components.
/**
* A global flag to toggle the URL enrichment and link previews in `MessageInput` components.
* By default, the feature is disabled. Can be overridden on Thread, MessageList level through additionalMessageInputProps
* or directly on MessageInput level through urlEnrichmentConfig.
*/
Expand All @@ -169,6 +170,12 @@ export type ChannelProps<
HeaderComponent?: ComponentContextValue<StreamChatGenerics>['HeaderComponent'];
/** A custom function to provide size configuration for image attachments */
imageAttachmentSizeHandler?: ImageAttachmentSizeHandler;
/**
* Allows to prevent triggering the channel.watch() call when mounting the component.
* That means that no channel data from the back-end will be received neither channel WS events will be delivered to the client.
* Preventing to initialize the channel on mount allows us to postpone the channel creation to a later point in time.
*/
initializeOnMount?: boolean;
/** Custom UI component handling how the message input is rendered, defaults to and accepts the same props as [MessageInputFlat](https://github.com/GetStream/stream-chat-react/blob/master/src/components/MessageInput/MessageInputFlat.tsx) */
Input?: ComponentContextValue<StreamChatGenerics>['Input'];
/** Custom component to render link previews in message input **/
Expand Down Expand Up @@ -312,6 +319,7 @@ const ChannelInner = <
dragAndDropWindow = false,
emojiData = defaultEmojiData,
enrichURLForPreviewConfig,
initializeOnMount = true,
LoadingErrorIndicator = DefaultLoadingErrorIndicator,
LoadingIndicator = DefaultLoadingIndicator,
maxNumberOfFiles,
Expand Down Expand Up @@ -478,7 +486,7 @@ const ChannelInner = <
};

(async () => {
if (!channel.initialized) {
if (!channel.initialized && initializeOnMount) {
try {
// if active channel has been set without id, we will create a temporary channel id from its member IDs
// to keep track of the /query request in progress. This is the same approach of generating temporary id
Expand Down Expand Up @@ -533,7 +541,7 @@ const ChannelInner = <
client.off('user.deleted', handleEvent);
notificationTimeouts.forEach(clearTimeout);
};
}, [channel.cid, doMarkReadRequest, channelConfig?.read_events]);
}, [channel.cid, doMarkReadRequest, channelConfig?.read_events, initializeOnMount]);

useEffect(() => {
if (!state.thread) return;
Expand Down
24 changes: 22 additions & 2 deletions src/components/Channel/__tests__/Channel.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -380,13 +380,33 @@ describe('Channel', () => {
jest.spyOn(channel, 'countUnread').mockImplementationOnce(() => 1);
const doMarkReadRequest = jest.fn();

renderComponent({
doMarkReadRequest,
await act(() => {
renderComponent({
doMarkReadRequest,
});
});

await waitFor(() => expect(doMarkReadRequest).toHaveBeenCalledTimes(1));
});

it('should not query the channel from the backend when initializeOnMount is disabled', async () => {
const watchSpy = jest.spyOn(channel, 'watch').mockImplementationOnce();
await act(() => {
renderComponent({
initializeOnMount: false,
});
});
await waitFor(() => expect(watchSpy).not.toHaveBeenCalled());
});

it('should query the channel from the backend when initializeOnMount is enabled (the default)', async () => {
const watchSpy = jest.spyOn(channel, 'watch').mockImplementationOnce();
await act(() => {
renderComponent();
});
await waitFor(() => expect(watchSpy).toHaveBeenCalledTimes(1));
});

describe('Children that consume the contexts set in Channel', () => {
it('should expose the emoji config', async () => {
let context;
Expand Down

0 comments on commit abc8ca6

Please sign in to comment.