Skip to content

Commit

Permalink
Merge branch 'master' into feat/remove-legacy-style-components
Browse files Browse the repository at this point in the history
  • Loading branch information
MartinCupela committed May 22, 2024
2 parents 5bab3ef + 28b6dfd commit 82174ba
Show file tree
Hide file tree
Showing 20 changed files with 231 additions and 178 deletions.
16 changes: 8 additions & 8 deletions docusaurus/docs/React/components/contexts/message-context.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -376,21 +376,21 @@ When true, show the reactions list component.
| ------- |
| boolean |

### sortReactionDetails
### reactionDetailsSort

Comparator function to sort the list of reacted users. Should have the same signature as an array's `sort` method.
Sort options to provide to a reactions query. Affects the order of reacted users in the default reactions modal.

| Type | Default |
| ---------------------------------------------------------- | ------------------ |
| (this: ReactionResponse, that: ReactionResponse) => number | alphabetical order |
| Type | Default |
| ---------------------- | --------------------------- |
| { created_at: number } | reverse chronological order |

### sortReactions

Comparator function to sort reactions. Should have the same signature as an array's `sort` method.

| Type | Default |
| -------------------------------------------------------- | ------------------ |
| (this: ReactionSummary, that: ReactionSummary) => number | alphabetical order |
| Type | Default |
| -------------------------------------------------------- | ------------------- |
| (this: ReactionSummary, that: ReactionSummary) => number | chronological order |

### threadList

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -533,21 +533,21 @@ is shown only when viewing unread messages.
| ------- | ------- |
| boolean | false |

### sortReactionDetails
### reactionDetailsSort

Comparator function to sort the list of reacted users. Should have the same signature as an array's `sort` method.
Sort options to provide to a reactions query. Affects the order of reacted users in the default reactions modal.

| Type | Default |
| ---------------------------------------------------------- | ------------------ |
| (this: ReactionResponse, that: ReactionResponse) => number | alphabetical order |
| Type | Default |
| ---------------------- | --------------------------- |
| { created_at: number } | reverse chronological order |

### sortReactions

Comparator function to sort reactions. Should have the same signature as the `sort` method for a string array.

| Type | Default |
| -------------------------------------------------------- | ------------------ |
| (this: ReactionSummary, that: ReactionSummary) => number | alphabetical order |
| Type | Default |
| -------------------------------------------------------- | ------------------- |
| (this: ReactionSummary, that: ReactionSummary) => number | chronological order |

### threadList

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -327,21 +327,21 @@ The scroll-to behavior when new messages appear. Use `'smooth'` for regular chat
| ------------------ | -------- |
| 'smooth' \| 'auto' | 'smooth' |

### sortReactionDetails
### reactionDetailsSort

Comparator function to sort the list of reacted users. Should have the same signature as an array's `sort` method.
Sort options to provide to a reactions query. Affects the order of reacted users in the default reactions modal.

| Type | Default |
| ---------------------------------------------------------- | ------------------ |
| (this: ReactionResponse, that: ReactionResponse) => number | alphabetical order |
| Type | Default |
| ---------------------- | --------------------------- |
| { created_at: number } | reverse chronological order |

### sortReactions

Comparator function to sort reactions. Should have the same signature as an array's `sort` method.

| Type | Default |
| -------------------------------------------------------- | ------------------ |
| (this: ReactionSummary, that: ReactionSummary) => number | alphabetical order |
| Type | Default |
| -------------------------------------------------------- | ------------------- |
| (this: ReactionSummary, that: ReactionSummary) => number | chronological order |

### threadList

Expand Down
16 changes: 8 additions & 8 deletions docusaurus/docs/React/components/message-components/message.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -362,21 +362,21 @@ Custom action handler to retry sending a message after a failed request.
| -------- | -------------------------------------------------------------------------------------------------------- |
| function | [ChannelActionContextValue['retrySendMessage']](../contexts/channel-action-context.mdx#retrysendmessage) |

### sortReactionDetails
### reactionDetailsSort

Comparator function to sort the list of reacted users. Should have the same signature as an array's `sort` method.
Sort options to provide to a reactions query. Affects the order of reacted users in the default reactions modal.

| Type | Default |
| ---------------------------------------------------------- | ------------------ |
| (this: ReactionResponse, that: ReactionResponse) => number | alphabetical order |
| Type | Default |
| ---------------------- | --------------------------- |
| { created_at: number } | reverse chronological order |

### sortReactions

Comparator function to sort reactions. Should have the same signature as the `sort` method for a string array.

| Type | Default |
| -------------------------------------------------------- | ------------------ |
| (this: ReactionSummary, that: ReactionSummary) => number | alphabetical order |
| Type | Default |
| -------------------------------------------------------- | ------------------- |
| (this: ReactionSummary, that: ReactionSummary) => number | chronological order |

### threadList

Expand Down
22 changes: 11 additions & 11 deletions docusaurus/docs/React/components/message-components/reactions.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ The SDK comes with built-in support for adding reactions to messages. The compon

## Sorting reactions

By default, reactions are sorted alphabetically by type. You can change this behavior by passing the `sortReactions` prop to the `MessageList` (or `VirtualizedMessageList`).
By default, reactions are sorted chronologically by the first time reaction type was used. You can change this behavior by passing the `sortReactions` prop to the `MessageList` (or `VirtualizedMessageList`).

In this example, we sort the reactions in the descending order by the number of users:

Expand All @@ -52,9 +52,9 @@ function sortByReactionCount(a, b) {
</Chat>;
```

Similarly, the `sortReactionDetails` prop can be passed to the `MessageList` (or `VirtualizedMessageList`) to sort the list of reacted users. The default implementation used by the reactions list modal dialog sorts users alphabetically by name.
For better performance, keep the sorting function memoized with `useCallback`, or declare it in either global or module scope.

For better performance, keep the sorting functions memoized with `useCallback`, or declare it in either global or module scope.
Similarly, the `reactionDetailsSort` object can be passed to the `MessageList` (or `VirtualizedMessageList`) to sort the list of reacted users. The default implementation used by the reactions list modal dialog sorts users in the reverse chronological order of their reactions.

## Customization

Expand Down Expand Up @@ -199,21 +199,21 @@ If true, adds a CSS class that reverses the horizontal positioning of the select
| ------- | ------- |
| boolean | false |

### sortReactionDetails
### reactionDetailsSort

Comparator function to sort the list of reacted users. Should have the same signature as an array's `sort` method. This prop overrides the function stored in `MessageContext`.
Sort options to provide to a reactions query. Affects the order of reacted users in the default reactions modal. This prop overrides the function stored in `MessageContext`.

| Type | Default |
| ---------------------------------------------------------- | ------------------ |
| (this: ReactionResponse, that: ReactionResponse) => number | alphabetical order |
| Type | Default |
| ---------------------- | --------------------------- |
| { created_at: number } | reverse chronological order |

### sortReactions

Comparator function to sort reactions. Should have the same signature as an array's `sort` method. This prop overrides the function stored in `MessageContext`.

| Type | Default |
| -------------------------------------------------------- | ------------------ |
| (this: ReactionSummary, that: ReactionSummary) => number | alphabetical order |
| Type | Default |
| -------------------------------------------------------- | ------------------- |
| (this: ReactionSummary, that: ReactionSummary) => number | chronological order |

## SimpleReactionsList Props

Expand Down
4 changes: 3 additions & 1 deletion examples/typescript/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import { ChannelFilters, ChannelOptions, ChannelSort, StreamChat } from 'stream-chat';
import { ChannelFilters, ChannelOptions, ChannelSort, StreamChat, UR } from 'stream-chat';
import {
Chat,
Channel,
Expand Down Expand Up @@ -37,6 +37,8 @@ type StreamChatGenerics = {
messageType: LocalMessageType;
reactionType: LocalReactionType;
userType: LocalUserType;
pollType: UR;
pollOptionType: UR;
};

const chatClient = StreamChat.getInstance<StreamChatGenerics>(apiKey);
Expand Down
3 changes: 3 additions & 0 deletions src/components/Message/Message.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ type MessageContextPropsToPick =
| 'onMentionsHoverMessage'
| 'onReactionListClick'
| 'reactionSelectorRef'
| 'reactionDetailsSort'
| 'showDetailedReactions'
| 'sortReactions'
| 'sortReactionDetails';
Expand Down Expand Up @@ -208,6 +209,7 @@ export const Message = <
onMentionsHover: propOnMentionsHover,
openThread: propOpenThread,
pinPermissions,
reactionDetailsSort,
retrySendMessage: propRetrySendMessage,
sortReactionDetails,
sortReactions,
Expand Down Expand Up @@ -308,6 +310,7 @@ export const Message = <
onUserClick={props.onUserClick}
onUserHover={props.onUserHover}
pinPermissions={props.pinPermissions}
reactionDetailsSort={reactionDetailsSort}
reactionSelectorRef={reactionSelectorRef}
readBy={props.readBy}
renderText={props.renderText}
Expand Down
41 changes: 17 additions & 24 deletions src/components/Message/hooks/__tests__/useReactionsFetcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,10 @@ import { ChatProvider } from '../../../../context/ChatContext';
import { generateChannel, generateMessage, getTestClient } from '../../../../mock-builders';
import { useReactionsFetcher } from '../useReactionsFetcher';

async function renderUseReactionsFetcherHook(channel = generateChannel(), notificationOpts) {
const client = await getTestClient();
function renderUseReactionsFetcherHook(client = getTestClient(), notificationOpts) {
const wrapper = ({ children }) => (
<ChatProvider value={{ client }}>
<ChannelStateProvider value={{ channel }}>{children}</ChannelStateProvider>
<ChannelStateProvider value={{ channel: generateChannel() }}>{children}</ChannelStateProvider>
</ChatProvider>
);

Expand All @@ -23,42 +22,36 @@ async function renderUseReactionsFetcherHook(channel = generateChannel(), notifi
describe('useReactionsFetcher custom hook', () => {
afterEach(() => jest.clearAllMocks());

it('should generate a function', async () => {
const fetchReactions = await renderUseReactionsFetcherHook();
it('should generate a function', () => {
const fetchReactions = renderUseReactionsFetcherHook();
expect(typeof fetchReactions).toBe('function');
});

it('generated function should make a request to fetch reactions', async () => {
const getReactionsMock = jest.fn(() => Promise.resolve({ reactions: [] }));
const channel = generateChannel({
getReactions: getReactionsMock,
});
const fetchReactions = await renderUseReactionsFetcherHook(channel);
const queryReactionsMock = jest.fn(() => Promise.resolve({ reactions: [] }));
const client = getTestClient({ queryReactions: queryReactionsMock });
const fetchReactions = renderUseReactionsFetcherHook(client);
await fetchReactions();
expect(getReactionsMock).toHaveBeenCalledTimes(1);
expect(queryReactionsMock).toHaveBeenCalledTimes(1);
});

it('generated function should make paged requests to fetch reactions', async () => {
const getReactionsMock = jest
const queryReactionsMock = jest
.fn()
.mockImplementationOnce(() => Promise.resolve({ reactions: Array(300) }))
.mockImplementationOnce(() => Promise.resolve({ reactions: [] }));
const channel = generateChannel({
getReactions: getReactionsMock,
});
const fetchReactions = await renderUseReactionsFetcherHook(channel);
.mockImplementationOnce(() => Promise.resolve({ next: '42', reactions: Array(20) }))
.mockImplementationOnce(() => Promise.resolve({ reactions: Array(20) }));
const client = getTestClient({ queryReactions: queryReactionsMock });
const fetchReactions = renderUseReactionsFetcherHook(client);
await fetchReactions();
expect(getReactionsMock).toHaveBeenCalledTimes(2);
expect(queryReactionsMock).toHaveBeenCalledTimes(2);
});

it('generated function should notify about errors', async () => {
const getReactionsMock = jest.fn(() => Promise.reject());
const channel = generateChannel({
getReactions: getReactionsMock,
});
const queryReactionsMock = jest.fn(() => Promise.reject());
const client = getTestClient({ queryReactions: queryReactionsMock });
const getErrorNotificationMock = jest.fn(() => 'Error message');
const notifyMock = jest.fn();
const fetchReactions = await renderUseReactionsFetcherHook(channel, {
const fetchReactions = renderUseReactionsFetcherHook(client, {
getErrorNotification: getErrorNotificationMock,
notify: notifyMock,
});
Expand Down
53 changes: 31 additions & 22 deletions src/components/Message/hooks/useReactionsFetcher.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { StreamMessage, useChannelStateContext, useTranslationContext } from '../../../context';
import { StreamMessage, useChatContext, useTranslationContext } from '../../../context';
import { DefaultStreamChatGenerics } from '../../../types/types';
import { Channel, ReactionResponse } from 'stream-chat';
import { ReactionResponse, ReactionSort, StreamChat } from 'stream-chat';
import { ReactionType } from '../../Reactions/types';

export const MAX_MESSAGE_REACTIONS_TO_FETCH = 1200;
export const MAX_MESSAGE_REACTIONS_TO_FETCH = 1000;

type FetchMessageReactionsNotifications<
StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics
Expand All @@ -17,13 +18,16 @@ export function useReactionsFetcher<
message: StreamMessage<StreamChatGenerics>,
notifications: FetchMessageReactionsNotifications<StreamChatGenerics> = {},
) {
const { channel } = useChannelStateContext<StreamChatGenerics>('useReactionFetcher');
const { client } = useChatContext('useRectionsFetcher');
const { t } = useTranslationContext('useReactionFetcher');
const { getErrorNotification, notify } = notifications;

return async () => {
return async (
reactionType?: ReactionType<StreamChatGenerics>,
sort?: ReactionSort<StreamChatGenerics>,
) => {
try {
return await fetchMessageReactions(channel, message.id);
return await fetchMessageReactions(client, message.id, reactionType, sort);
} catch (e) {
const errorMessage = getErrorNotification?.(message);
notify?.(errorMessage || t('Error fetching reactions'), 'error');
Expand All @@ -34,23 +38,28 @@ export function useReactionsFetcher<

async function fetchMessageReactions<
StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics
>(channel: Channel<StreamChatGenerics>, messageId: string) {
>(
client: StreamChat<StreamChatGenerics>,
messageId: string,
reactionType?: ReactionType<StreamChatGenerics>,
sort?: ReactionSort<StreamChatGenerics>,
) {
const reactions: ReactionResponse<StreamChatGenerics>[] = [];
const limit = 300;
let offset = 0;
const reactionsLimit = MAX_MESSAGE_REACTIONS_TO_FETCH;
let lastPageSize = limit;

while (lastPageSize === limit && reactions.length < reactionsLimit) {
const response = await channel.getReactions(messageId, {
limit,
offset,
});
lastPageSize = response.reactions.length;
if (lastPageSize > 0) {
reactions.push(...response.reactions);
}
offset += lastPageSize;
const limit = 25;
let next: string | undefined;
let hasNext = true;

while (hasNext && reactions.length < MAX_MESSAGE_REACTIONS_TO_FETCH) {
const response = await client.queryReactions(
messageId,
reactionType ? { type: reactionType } : {},
sort,
{ limit, next },
);

reactions.push(...response.reactions);
next = response.next;
hasNext = Boolean(next);
}

return reactions;
Expand Down
10 changes: 7 additions & 3 deletions src/components/Message/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { TFunction } from 'i18next';
import type { UserResponse } from 'stream-chat';
import type { ReactionSort, UserResponse } from 'stream-chat';

import type { PinPermissions, UserEventHandler } from './hooks';
import type { MessageActionsArray } from './utils';
Expand Down Expand Up @@ -88,6 +88,8 @@ export type MessageProps<
openThread?: ChannelActionContextValue<StreamChatGenerics>['openThread'];
/** @deprecated in favor of `channelCapabilities - The user roles allowed to pin messages in various channel types */
pinPermissions?: PinPermissions;
/** Sort options to provide to a reactions query */
reactionDetailsSort?: ReactionSort<StreamChatGenerics>;
/** A list of users that have read this Message if the message is the last one and was posted by my user */
readBy?: UserResponse<StreamChatGenerics>[];
/** Custom function to render message text content, defaults to the renderText function: [utils](https://github.com/GetStream/stream-chat-react/blob/master/src/utils.ts) */
Expand All @@ -98,9 +100,11 @@ export type MessageProps<
) => JSX.Element | null;
/** Custom retry send message handler to override default in [ChannelActionContext](https://getstream.io/chat/docs/sdk/react/contexts/channel_action_context/) */
retrySendMessage?: ChannelActionContextValue<StreamChatGenerics>['retrySendMessage'];
/** Comparator function to sort the list of reacted users, defaults to alphabetical order */
/** Comparator function to sort the list of reacted users
* @deprecated use `reactionDetailsSort` instead
*/
sortReactionDetails?: ReactionDetailsComparator;
/** Comparator function to sort reactions, defaults to alphabetical order */
/** Comparator function to sort reactions, defaults to chronological order */
sortReactions?: ReactionsComparator;
/** Whether the Message is in a Thread */
threadList?: boolean;
Expand Down
Loading

0 comments on commit 82174ba

Please sign in to comment.