Skip to content

Commit a364279

Browse files
committed
feat: allow for custom ReminderNotification component
1 parent 74c73d5 commit a364279

7 files changed

Lines changed: 85 additions & 16 deletions

File tree

src/components/Channel/Channel.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ type ChannelPropsForwardedToComponentContext = Pick<
140140
| 'ReactionSelector'
141141
| 'ReactionsList'
142142
| 'ReactionsListModal'
143+
| 'ReminderNotification'
143144
| 'SendButton'
144145
| 'StartRecordingAudioButton'
145146
| 'TextareaComposer'
@@ -1226,6 +1227,7 @@ const ChannelInner = (
12261227
ReactionSelector: props.ReactionSelector,
12271228
ReactionsList: props.ReactionsList,
12281229
ReactionsListModal: props.ReactionsListModal,
1230+
ReminderNotification: props.ReminderNotification,
12291231
SendButton: props.SendButton,
12301232
StartRecordingAudioButton: props.StartRecordingAudioButton,
12311233
StopAIGenerationButton: props.StopAIGenerationButton,
@@ -1289,6 +1291,7 @@ const ChannelInner = (
12891291
props.ReactionSelector,
12901292
props.ReactionsList,
12911293
props.ReactionsListModal,
1294+
props.ReminderNotification,
12921295
props.SendButton,
12931296
props.StartRecordingAudioButton,
12941297
props.StopAIGenerationButton,

src/components/Message/MessageSimple.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ import type { MessageUIComponentProps } from './types';
3737

3838
import { StreamedMessageText as DefaultStreamedMessageText } from './StreamedMessageText';
3939
import { isDateSeparatorMessage } from '../MessageList';
40-
import { ReminderNotification } from './ReminderNotification';
40+
import { ReminderNotification as DefaultReminderNotification } from './ReminderNotification';
4141
import { useMessageReminder } from './hooks';
4242

4343
type MessageSimpleWithContextProps = MessageContextValue;
@@ -81,6 +81,7 @@ const MessageSimpleWithContext = (props: MessageSimpleWithContextProps) => {
8181
MessageStatus = DefaultMessageStatus,
8282
MessageTimestamp = DefaultMessageTimestamp,
8383
ReactionsList = DefaultReactionList,
84+
ReminderNotification = DefaultReminderNotification,
8485
StreamedMessageText = DefaultStreamedMessageText,
8586
PinIndicator,
8687
} = useComponentContext('MessageSimple');

src/components/Message/__tests__/MessageSimple.test.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import {
3636
useMockedApis,
3737
} from '../../../mock-builders';
3838
import { MessageBouncePrompt } from '../../MessageBounce';
39+
import { generateReminderResponse } from '../../../mock-builders/generator/reminder';
3940

4041
expect.extend(toHaveNoViolations);
4142

@@ -250,6 +251,28 @@ describe('<MessageSimple />', () => {
250251
expect(results).toHaveNoViolations();
251252
});
252253

254+
it('should render custom ReminderNotification component when one is given', async () => {
255+
const message = generateAliceMessage({ reminder: generateReminderResponse() });
256+
client.reminders.hydrateState([message]);
257+
const testId = 'custom-reminder-notification';
258+
const CustomReminderNotification = () => <div data-testid={testId} />;
259+
260+
const { container } = await renderMessageSimple({
261+
channelConfigOverrides: {
262+
user_message_reminder: true,
263+
},
264+
components: {
265+
ReminderNotification: CustomReminderNotification,
266+
},
267+
message,
268+
});
269+
270+
expect(await screen.findByTestId(testId)).toBeInTheDocument();
271+
272+
const results = await axe(container);
273+
expect(results).toHaveNoViolations();
274+
});
275+
253276
// FIXME: test relying on deprecated channel config parameter
254277
it('should render reaction list even though sending reactions is disabled in channel config', async () => {
255278
const reactions = [generateReaction({ user: bob })];

src/components/Message/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export * from './MessageStatus';
1010
export * from './MessageText';
1111
export * from './MessageTimestamp';
1212
export * from './QuotedMessage';
13+
export * from './ReminderNotification';
1314
export * from './renderText';
1415
export * from './types';
1516
export * from './utils';

src/context/ComponentContext.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import type {
3737
ReactionsListModalProps,
3838
ReactionsListProps,
3939
RecordingPermissionDeniedNotificationProps,
40+
ReminderNotificationProps,
4041
SendButtonProps,
4142
StartRecordingAudioButtonProps,
4243
StreamedMessageTextProps,
@@ -172,6 +173,7 @@ export type ComponentContextValue = {
172173
/** Custom UI component to display the reactions modal, defaults to and accepts same props as: [ReactionsListModal](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Reactions/ReactionsListModal.tsx) */
173174
ReactionsListModal?: React.ComponentType<ReactionsListModalProps>;
174175
RecordingPermissionDeniedNotification?: React.ComponentType<RecordingPermissionDeniedNotificationProps>;
176+
ReminderNotification?: React.ComponentType<ReminderNotificationProps>;
175177
/** Custom component to display the search UI, defaults to and accepts same props as: [Search](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Search/Search.tsx) */
176178
Search?: React.ComponentType<SearchProps>;
177179
/** Custom component to display the UI where the searched string is entered, defaults to and accepts same props as: [SearchBar](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Search/SearchBar/SearchBar.tsx) */
Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,23 @@
11
import { nanoid } from 'nanoid';
22

3-
export const generateMessage = (options) => ({
4-
__html: '<p>regular</p>',
5-
attachments: [],
6-
created_at: new Date(),
7-
html: '<p>regular</p>',
8-
id: nanoid(),
9-
mentioned_users: [],
10-
pinned_at: null,
11-
status: 'received',
12-
text: nanoid(),
13-
type: 'regular',
14-
updated_at: new Date(),
15-
user: null,
16-
...options,
17-
});
3+
export const generateMessage = (options) => {
4+
const data = {
5+
__html: '<p>regular</p>',
6+
attachments: [],
7+
created_at: new Date(),
8+
html: '<p>regular</p>',
9+
id: nanoid(),
10+
mentioned_users: [],
11+
pinned_at: null,
12+
status: 'received',
13+
text: nanoid(),
14+
type: 'regular',
15+
updated_at: new Date(),
16+
user: null,
17+
...options,
18+
};
19+
if (data.reminder) {
20+
data.reminder.message_id = data.id;
21+
}
22+
return data;
23+
};
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import type { ReminderResponse } from 'stream-chat';
2+
3+
const baseData = {
4+
channel_cid: 'channel_cid',
5+
message_id: 'message_id',
6+
user_id: 'user_id',
7+
} as const;
8+
9+
export const generateReminderResponse = ({
10+
data,
11+
scheduleOffsetMs,
12+
}: {
13+
data?: Partial<ReminderResponse>;
14+
scheduleOffsetMs?: number;
15+
} = {}): ReminderResponse => {
16+
const created_at = new Date().toISOString();
17+
const basePayload: ReminderResponse = {
18+
...baseData,
19+
created_at,
20+
message: { id: baseData.message_id, type: 'regular' },
21+
updated_at: created_at,
22+
user: { id: baseData.user_id },
23+
};
24+
if (typeof scheduleOffsetMs === 'number') {
25+
basePayload.remind_at = new Date(
26+
new Date(created_at).getTime() + scheduleOffsetMs,
27+
).toISOString();
28+
}
29+
return {
30+
...basePayload,
31+
...data,
32+
};
33+
};

0 commit comments

Comments
 (0)