stream-chat-react-native-core 10.0.0-rc.3 → 10.0.0-rc.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/commonjs/components/AttachmentPicker/components/AttachmentMediaPicker/AttachmentPickerItem.js +4 -1
- package/lib/commonjs/components/AttachmentPicker/components/AttachmentMediaPicker/AttachmentPickerItem.js.map +1 -1
- package/lib/commonjs/components/Channel/Channel.js +1 -0
- package/lib/commonjs/components/Channel/Channel.js.map +1 -1
- package/lib/commonjs/components/Channel/ChannelShell.js +8 -1
- package/lib/commonjs/components/Channel/ChannelShell.js.map +1 -1
- package/lib/commonjs/components/Channel/hooks/useChannelLifecycle.js +2 -2
- package/lib/commonjs/components/Channel/hooks/useChannelLifecycle.js.map +1 -1
- package/lib/commonjs/components/Channel/hooks/useJumpToLatestOnOpen.js +26 -0
- package/lib/commonjs/components/Channel/hooks/useJumpToLatestOnOpen.js.map +1 -0
- package/lib/commonjs/components/ChannelList/hooks/usePaginatedChannels.js +1 -1
- package/lib/commonjs/components/ChannelList/hooks/usePaginatedChannels.js.map +1 -1
- package/lib/commonjs/components/ChannelPreview/ChannelMessagePreviewDeliveryStatus.js +7 -0
- package/lib/commonjs/components/ChannelPreview/ChannelMessagePreviewDeliveryStatus.js.map +1 -1
- package/lib/commonjs/components/ChannelPreview/ChannelPreviewMessage.js +4 -5
- package/lib/commonjs/components/ChannelPreview/ChannelPreviewMessage.js.map +1 -1
- package/lib/commonjs/components/Message/Message.js.map +1 -1
- package/lib/commonjs/components/Message/MessageItemView/Headers/SentToChannelHeader.js +6 -32
- package/lib/commonjs/components/Message/MessageItemView/Headers/SentToChannelHeader.js.map +1 -1
- package/lib/commonjs/components/Message/MessageItemView/MessageContent.js +2 -2
- package/lib/commonjs/components/Message/MessageItemView/MessageContent.js.map +1 -1
- package/lib/commonjs/components/Message/MessageItemView/MessageStatus.js +29 -9
- package/lib/commonjs/components/Message/MessageItemView/MessageStatus.js.map +1 -1
- package/lib/commonjs/components/Message/hooks/useOpenThreadAtMessage.js +57 -0
- package/lib/commonjs/components/Message/hooks/useOpenThreadAtMessage.js.map +1 -0
- package/lib/commonjs/components/Message/utils/messageActions.js +1 -1
- package/lib/commonjs/components/Message/utils/messageActions.js.map +1 -1
- package/lib/commonjs/components/MessageInput/components/AttachmentPreview/AttachmentUploadPreviewList.js +12 -8
- package/lib/commonjs/components/MessageInput/components/AttachmentPreview/AttachmentUploadPreviewList.js.map +1 -1
- package/lib/commonjs/components/MessageInput/components/AttachmentPreview/AudioAttachmentUploadPreview.js +3 -2
- package/lib/commonjs/components/MessageInput/components/AttachmentPreview/AudioAttachmentUploadPreview.js.map +1 -1
- package/lib/commonjs/components/MessageInput/hooks/useAudioRecorder.js +2 -2
- package/lib/commonjs/components/MessageInput/hooks/useAudioRecorder.js.map +1 -1
- package/lib/commonjs/components/MessageList/InlineUnreadIndicator.js +2 -2
- package/lib/commonjs/components/MessageList/InlineUnreadIndicator.js.map +1 -1
- package/lib/commonjs/components/MessageList/MessageList.js +1 -4
- package/lib/commonjs/components/MessageList/MessageList.js.map +1 -1
- package/lib/commonjs/components/MessageList/hooks/useMessageListFocus.js +18 -2
- package/lib/commonjs/components/MessageList/hooks/useMessageListFocus.js.map +1 -1
- package/lib/commonjs/components/Thread/components/ThreadFooterComponent.js +1 -2
- package/lib/commonjs/components/Thread/components/ThreadFooterComponent.js.map +1 -1
- package/lib/commonjs/components/index.js +12 -0
- package/lib/commonjs/components/index.js.map +1 -1
- package/lib/commonjs/contexts/channelPinnedMessageListContext/ChannelPinnedMessageListContext.js +0 -15
- package/lib/commonjs/contexts/channelPinnedMessageListContext/ChannelPinnedMessageListContext.js.map +1 -1
- package/lib/commonjs/contexts/messageContext/MessageContext.js.map +1 -1
- package/lib/commonjs/contexts/messageListItemContext/MessageListItemContext.js.map +1 -1
- package/lib/commonjs/index.js +0 -12
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/types/types.js.map +1 -1
- package/lib/commonjs/version.json +1 -1
- package/lib/module/components/AttachmentPicker/components/AttachmentMediaPicker/AttachmentPickerItem.js +4 -1
- package/lib/module/components/AttachmentPicker/components/AttachmentMediaPicker/AttachmentPickerItem.js.map +1 -1
- package/lib/module/components/Channel/Channel.js +1 -0
- package/lib/module/components/Channel/Channel.js.map +1 -1
- package/lib/module/components/Channel/ChannelShell.js +8 -1
- package/lib/module/components/Channel/ChannelShell.js.map +1 -1
- package/lib/module/components/Channel/hooks/useChannelLifecycle.js +2 -2
- package/lib/module/components/Channel/hooks/useChannelLifecycle.js.map +1 -1
- package/lib/module/components/Channel/hooks/useJumpToLatestOnOpen.js +26 -0
- package/lib/module/components/Channel/hooks/useJumpToLatestOnOpen.js.map +1 -0
- package/lib/module/components/ChannelList/hooks/usePaginatedChannels.js +1 -1
- package/lib/module/components/ChannelList/hooks/usePaginatedChannels.js.map +1 -1
- package/lib/module/components/ChannelPreview/ChannelMessagePreviewDeliveryStatus.js +7 -0
- package/lib/module/components/ChannelPreview/ChannelMessagePreviewDeliveryStatus.js.map +1 -1
- package/lib/module/components/ChannelPreview/ChannelPreviewMessage.js +4 -5
- package/lib/module/components/ChannelPreview/ChannelPreviewMessage.js.map +1 -1
- package/lib/module/components/Message/Message.js.map +1 -1
- package/lib/module/components/Message/MessageItemView/Headers/SentToChannelHeader.js +6 -32
- package/lib/module/components/Message/MessageItemView/Headers/SentToChannelHeader.js.map +1 -1
- package/lib/module/components/Message/MessageItemView/MessageContent.js +2 -2
- package/lib/module/components/Message/MessageItemView/MessageContent.js.map +1 -1
- package/lib/module/components/Message/MessageItemView/MessageStatus.js +29 -9
- package/lib/module/components/Message/MessageItemView/MessageStatus.js.map +1 -1
- package/lib/module/components/Message/hooks/useOpenThreadAtMessage.js +57 -0
- package/lib/module/components/Message/hooks/useOpenThreadAtMessage.js.map +1 -0
- package/lib/module/components/Message/utils/messageActions.js +1 -1
- package/lib/module/components/Message/utils/messageActions.js.map +1 -1
- package/lib/module/components/MessageInput/components/AttachmentPreview/AttachmentUploadPreviewList.js +12 -8
- package/lib/module/components/MessageInput/components/AttachmentPreview/AttachmentUploadPreviewList.js.map +1 -1
- package/lib/module/components/MessageInput/components/AttachmentPreview/AudioAttachmentUploadPreview.js +3 -2
- package/lib/module/components/MessageInput/components/AttachmentPreview/AudioAttachmentUploadPreview.js.map +1 -1
- package/lib/module/components/MessageInput/hooks/useAudioRecorder.js +2 -2
- package/lib/module/components/MessageInput/hooks/useAudioRecorder.js.map +1 -1
- package/lib/module/components/MessageList/InlineUnreadIndicator.js +2 -2
- package/lib/module/components/MessageList/InlineUnreadIndicator.js.map +1 -1
- package/lib/module/components/MessageList/MessageList.js +1 -4
- package/lib/module/components/MessageList/MessageList.js.map +1 -1
- package/lib/module/components/MessageList/hooks/useMessageListFocus.js +18 -2
- package/lib/module/components/MessageList/hooks/useMessageListFocus.js.map +1 -1
- package/lib/module/components/Thread/components/ThreadFooterComponent.js +1 -2
- package/lib/module/components/Thread/components/ThreadFooterComponent.js.map +1 -1
- package/lib/module/components/index.js +12 -0
- package/lib/module/components/index.js.map +1 -1
- package/lib/module/contexts/channelPinnedMessageListContext/ChannelPinnedMessageListContext.js +0 -15
- package/lib/module/contexts/channelPinnedMessageListContext/ChannelPinnedMessageListContext.js.map +1 -1
- package/lib/module/contexts/messageContext/MessageContext.js.map +1 -1
- package/lib/module/contexts/messageListItemContext/MessageListItemContext.js.map +1 -1
- package/lib/module/index.js +0 -12
- package/lib/module/index.js.map +1 -1
- package/lib/module/types/types.js.map +1 -1
- package/lib/module/version.json +1 -1
- package/lib/typescript/ai-components/services/media-picker-service/index.d.ts +1 -1
- package/lib/typescript/components/Channel/Channel.d.ts.map +1 -1
- package/lib/typescript/components/Channel/ChannelShell.d.ts +2 -0
- package/lib/typescript/components/Channel/ChannelShell.d.ts.map +1 -1
- package/lib/typescript/components/Channel/hooks/useChannelLifecycle.d.ts.map +1 -1
- package/lib/typescript/components/Channel/hooks/useJumpToLatestOnOpen.d.ts +15 -0
- package/lib/typescript/components/Channel/hooks/useJumpToLatestOnOpen.d.ts.map +1 -0
- package/lib/typescript/components/ChannelPreview/ChannelMessagePreviewDeliveryStatus.d.ts.map +1 -1
- package/lib/typescript/components/ChannelPreview/ChannelPreviewMessage.d.ts.map +1 -1
- package/lib/typescript/components/Message/Message.d.ts +2 -1
- package/lib/typescript/components/Message/Message.d.ts.map +1 -1
- package/lib/typescript/components/Message/MessageItemView/Headers/SentToChannelHeader.d.ts.map +1 -1
- package/lib/typescript/components/Message/MessageItemView/MessageContent.d.ts.map +1 -1
- package/lib/typescript/components/Message/MessageItemView/MessageStatus.d.ts +6 -0
- package/lib/typescript/components/Message/MessageItemView/MessageStatus.d.ts.map +1 -1
- package/lib/typescript/components/Message/hooks/useOpenThreadAtMessage.d.ts +14 -0
- package/lib/typescript/components/Message/hooks/useOpenThreadAtMessage.d.ts.map +1 -0
- package/lib/typescript/components/MessageInput/components/AttachmentPreview/AttachmentUploadPreviewList.d.ts.map +1 -1
- package/lib/typescript/components/MessageInput/components/AttachmentPreview/AudioAttachmentUploadPreview.d.ts.map +1 -1
- package/lib/typescript/components/MessageList/MessageList.d.ts.map +1 -1
- package/lib/typescript/components/MessageList/hooks/useMessageListFocus.d.ts +5 -2
- package/lib/typescript/components/MessageList/hooks/useMessageListFocus.d.ts.map +1 -1
- package/lib/typescript/components/Thread/components/ThreadFooterComponent.d.ts.map +1 -1
- package/lib/typescript/components/index.d.ts +1 -0
- package/lib/typescript/components/index.d.ts.map +1 -1
- package/lib/typescript/contexts/channelPinnedMessageListContext/ChannelPinnedMessageListContext.d.ts.map +1 -1
- package/lib/typescript/contexts/messageContext/MessageContext.d.ts +2 -1
- package/lib/typescript/contexts/messageContext/MessageContext.d.ts.map +1 -1
- package/lib/typescript/contexts/messageListItemContext/MessageListItemContext.d.ts +10 -6
- package/lib/typescript/contexts/messageListItemContext/MessageListItemContext.d.ts.map +1 -1
- package/lib/typescript/i18n/keys.d.ts +2 -2
- package/lib/typescript/i18n/keys.d.ts.map +1 -1
- package/lib/typescript/index.d.ts +0 -1
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/types/types.d.ts +0 -4
- package/lib/typescript/types/types.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/components/AttachmentPicker/components/AttachmentMediaPicker/AttachmentPickerItem.tsx +1 -1
- package/src/components/AttachmentPicker/components/AttachmentMediaPicker/__tests__/AttachmentPickerItem.test.tsx +29 -1
- package/src/components/Channel/Channel.tsx +1 -0
- package/src/components/Channel/ChannelShell.tsx +12 -0
- package/src/components/Channel/__tests__/Channel.test.tsx +142 -7
- package/src/components/Channel/hooks/useChannelLifecycle.ts +8 -4
- package/src/components/Channel/hooks/useCreateOwnCapabilitiesContext.ts +1 -1
- package/src/components/Channel/hooks/useJumpToLatestOnOpen.ts +42 -0
- package/src/components/ChannelList/__tests__/ChannelList.test.tsx +28 -0
- package/src/components/ChannelList/hooks/usePaginatedChannels.ts +1 -1
- package/src/components/ChannelPreview/ChannelMessagePreviewDeliveryStatus.tsx +8 -0
- package/src/components/ChannelPreview/ChannelPreviewMessage.tsx +2 -7
- package/src/components/ChannelPreview/__tests__/ChannelPreviewView.test.tsx +41 -0
- package/src/components/Message/Message.tsx +5 -2
- package/src/components/Message/MessageItemView/Headers/SentToChannelHeader.tsx +4 -32
- package/src/components/Message/MessageItemView/MessageContent.tsx +3 -2
- package/src/components/Message/MessageItemView/MessageStatus.tsx +43 -4
- package/src/components/Message/MessageItemView/__tests__/MessageContent.test.tsx +68 -1
- package/src/components/Message/MessageItemView/__tests__/MessageStatus.test.tsx +57 -0
- package/src/components/Message/hooks/useOpenThreadAtMessage.ts +50 -0
- package/src/components/Message/utils/__tests__/messageActions.test.ts +57 -0
- package/src/components/Message/utils/messageActions.ts +1 -1
- package/src/components/MessageInput/__tests__/AttachmentUploadPreviewList.test.tsx +34 -6
- package/src/components/MessageInput/__tests__/AudioAttachmentUploadPreview.test.tsx +24 -3
- package/src/components/MessageInput/__tests__/SendMessageDisallowedIndicator.test.tsx +3 -6
- package/src/components/MessageInput/components/AttachmentPreview/AttachmentUploadPreviewList.tsx +19 -8
- package/src/components/MessageInput/components/AttachmentPreview/AudioAttachmentUploadPreview.tsx +2 -5
- package/src/components/MessageInput/hooks/useAudioRecorder.tsx +2 -2
- package/src/components/MessageList/InlineUnreadIndicator.tsx +2 -2
- package/src/components/MessageList/MessageList.tsx +1 -5
- package/src/components/MessageList/__tests__/InlineUnreadIndicator.test.tsx +2 -2
- package/src/components/MessageList/__tests__/MessageList.test.tsx +252 -6
- package/src/components/MessageList/hooks/useMessageListFocus.ts +22 -2
- package/src/components/Thread/__tests__/Thread.test.tsx +131 -1
- package/src/components/Thread/components/ThreadFooterComponent.tsx +0 -1
- package/src/components/index.ts +1 -0
- package/src/contexts/channelPinnedMessageListContext/ChannelPinnedMessageListContext.tsx +5 -14
- package/src/contexts/messageContext/MessageContext.tsx +2 -1
- package/src/contexts/messageListItemContext/MessageListItemContext.tsx +15 -6
- package/src/i18n/__tests__/catalog.fixture.json +2 -2
- package/src/i18n/keys.ts +2 -2
- package/src/index.ts +0 -1
- package/src/types/stream-chat-common-custom-data.d.ts +0 -3
- package/src/types/types.ts +0 -5
- package/src/version.json +1 -1
- package/lib/commonjs/utils/setupCommandUIMiddlewares.js +0 -34
- package/lib/commonjs/utils/setupCommandUIMiddlewares.js.map +0 -1
- package/lib/module/utils/setupCommandUIMiddlewares.js +0 -34
- package/lib/module/utils/setupCommandUIMiddlewares.js.map +0 -1
- package/lib/typescript/utils/setupCommandUIMiddlewares.d.ts +0 -3
- package/lib/typescript/utils/setupCommandUIMiddlewares.d.ts.map +0 -1
- package/src/utils/setupCommandUIMiddlewares.ts +0 -31
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { StyleSheet, View } from 'react-native';
|
|
3
3
|
|
|
4
|
-
import { cleanup, render, screen, waitFor } from '@testing-library/react-native';
|
|
4
|
+
import { cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react-native';
|
|
5
5
|
import type { Channel as ChannelType, StreamChat } from 'stream-chat';
|
|
6
6
|
|
|
7
7
|
import { WithComponents } from '../../../../contexts/componentsContext/ComponentsContext';
|
|
@@ -382,6 +382,73 @@ describe('MessageContent', () => {
|
|
|
382
382
|
expect(contentContainerStyle.paddingBottom).toBeGreaterThan(0);
|
|
383
383
|
});
|
|
384
384
|
|
|
385
|
+
describe('pressing a quote', () => {
|
|
386
|
+
const renderQuote = ({
|
|
387
|
+
quotedMessage,
|
|
388
|
+
}: {
|
|
389
|
+
quotedMessage: ReturnType<typeof generateMessage>;
|
|
390
|
+
}) => {
|
|
391
|
+
const goToMessage = jest.fn();
|
|
392
|
+
render(
|
|
393
|
+
<Chat client={chatClient}>
|
|
394
|
+
<Channel channel={channel}>
|
|
395
|
+
<Message
|
|
396
|
+
goToMessage={goToMessage}
|
|
397
|
+
groupStyles={['bottom']}
|
|
398
|
+
message={generateMessage({ quoted_message: quotedMessage, user })}
|
|
399
|
+
/>
|
|
400
|
+
</Channel>
|
|
401
|
+
</Chat>,
|
|
402
|
+
);
|
|
403
|
+
return { goToMessage };
|
|
404
|
+
};
|
|
405
|
+
|
|
406
|
+
const threadOnlyReply = () => {
|
|
407
|
+
const parentMessage = generateMessage({ cid: channel.cid, user });
|
|
408
|
+
const thread = chatClient.threads.ensure({ channel, parentMessage });
|
|
409
|
+
const reply = generateMessage({ parent_id: parentMessage.id, text: 'quoted message', user });
|
|
410
|
+
return { reply, thread };
|
|
411
|
+
};
|
|
412
|
+
|
|
413
|
+
it('passes the quoted message to goToMessage when the quote is pressed', async () => {
|
|
414
|
+
const quotedMessage = generateMessage({ text: 'quoted message', user });
|
|
415
|
+
const { goToMessage } = renderQuote({ quotedMessage });
|
|
416
|
+
|
|
417
|
+
fireEvent.press(await screen.findByText('quoted message'));
|
|
418
|
+
|
|
419
|
+
await waitFor(() =>
|
|
420
|
+
expect(goToMessage).toHaveBeenCalledWith(
|
|
421
|
+
quotedMessage.id,
|
|
422
|
+
expect.objectContaining({ id: quotedMessage.id }),
|
|
423
|
+
),
|
|
424
|
+
);
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
it('opens the thread from the header of a reply also sent to the channel', async () => {
|
|
428
|
+
const onAlsoSentToChannelHeaderPress = jest.fn();
|
|
429
|
+
const { reply, thread } = threadOnlyReply();
|
|
430
|
+
render(
|
|
431
|
+
<Chat client={chatClient}>
|
|
432
|
+
<Channel
|
|
433
|
+
channel={channel}
|
|
434
|
+
onAlsoSentToChannelHeaderPress={onAlsoSentToChannelHeaderPress}
|
|
435
|
+
>
|
|
436
|
+
<Message groupStyles={['bottom']} message={{ ...reply, show_in_channel: true }} />
|
|
437
|
+
</Channel>
|
|
438
|
+
</Chat>,
|
|
439
|
+
);
|
|
440
|
+
|
|
441
|
+
fireEvent.press(await screen.findByText('View'));
|
|
442
|
+
|
|
443
|
+
await waitFor(() =>
|
|
444
|
+
expect(onAlsoSentToChannelHeaderPress).toHaveBeenCalledWith({
|
|
445
|
+
targetedMessageId: reply.id,
|
|
446
|
+
thread,
|
|
447
|
+
}),
|
|
448
|
+
);
|
|
449
|
+
});
|
|
450
|
+
});
|
|
451
|
+
|
|
385
452
|
it('does not render the quoted reply for an ephemeral giphy preview', async () => {
|
|
386
453
|
const user = generateUser();
|
|
387
454
|
const message = generateMessage({
|
|
@@ -84,6 +84,63 @@ describe('MessageStatus', () => {
|
|
|
84
84
|
},
|
|
85
85
|
);
|
|
86
86
|
|
|
87
|
+
it.each([
|
|
88
|
+
['renders', 'regular', 1],
|
|
89
|
+
['renders no', 'deleted', 0],
|
|
90
|
+
])('%s status for a %s message', async (_, type, expectedCount) => {
|
|
91
|
+
const message = generateMessage({ user: user1 });
|
|
92
|
+
const { queryAllByLabelText } = renderMessageStatus({
|
|
93
|
+
delivered: true,
|
|
94
|
+
message: { ...message, status: 'received', type },
|
|
95
|
+
read: true,
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
await act(async () => {
|
|
99
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
expect(queryAllByLabelText('Read')).toHaveLength(expectedCount);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
describe('with read events disabled', () => {
|
|
106
|
+
beforeEach(() => {
|
|
107
|
+
chatClient.config.set({ channel: { readEvents: { enabled: false } } });
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it.each([
|
|
111
|
+
[false, false, 'Sent'],
|
|
112
|
+
[true, false, 'Delivered'],
|
|
113
|
+
[true, true, 'Read'],
|
|
114
|
+
])(
|
|
115
|
+
'renders no status when delivered is %s and read is %s',
|
|
116
|
+
async (delivered, read, accessibilityLabel) => {
|
|
117
|
+
const message = generateMessage({ user: user1 });
|
|
118
|
+
const { queryByLabelText } = renderMessageStatus({
|
|
119
|
+
delivered,
|
|
120
|
+
message: { ...message, status: 'received' },
|
|
121
|
+
read,
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
await act(async () => {
|
|
125
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
expect(queryByLabelText(accessibilityLabel)).toBeNull();
|
|
129
|
+
},
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
it('still renders the sending status', async () => {
|
|
133
|
+
const message = generateMessage({ user: user1 });
|
|
134
|
+
const { getByLabelText } = renderMessageStatus({
|
|
135
|
+
message: { ...message, status: 'sending' },
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
await waitFor(() => {
|
|
139
|
+
expect(getByLabelText('Sending')).toBeTruthy();
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
|
|
87
144
|
describe('reactive receipt state', () => {
|
|
88
145
|
// Two own messages, older first. `olderMessage` is the one under test throughout: the whole
|
|
89
146
|
// point of sourcing from `lastReadRefByOthers` is that it reports correctly even though no
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { useChannelContext } from '../../../contexts/channelContext/ChannelContext';
|
|
2
|
+
import { useChatContext } from '../../../contexts/chatContext/ChatContext';
|
|
3
|
+
import { useMessagesContext } from '../../../contexts/messagesContext/MessagesContext';
|
|
4
|
+
import { useStableCallback } from '../../../hooks';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Returns a function that opens the thread holding a message, at that message, through the
|
|
8
|
+
* `onAlsoSentToChannelHeaderPress` handler. The thread is taken from the client when it is
|
|
9
|
+
* registered there; otherwise its parent message is read from the message store, or fetched, and a
|
|
10
|
+
* thread is built from it. Opening the thread loads whatever else it is missing.
|
|
11
|
+
*
|
|
12
|
+
* @returns The function, which takes the thread's `parentId` and the `targetedMessageId` to open
|
|
13
|
+
* the thread at. It does nothing without an `onAlsoSentToChannelHeaderPress` handler.
|
|
14
|
+
*/
|
|
15
|
+
export const useOpenThreadAtMessage = () => {
|
|
16
|
+
const { onAlsoSentToChannelHeaderPress } = useMessagesContext();
|
|
17
|
+
const { client } = useChatContext();
|
|
18
|
+
const { channel } = useChannelContext();
|
|
19
|
+
|
|
20
|
+
return useStableCallback(
|
|
21
|
+
async ({ parentId, targetedMessageId }: { parentId: string; targetedMessageId: string }) => {
|
|
22
|
+
if (!onAlsoSentToChannelHeaderPress) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const registeredThread = client.threads.get(parentId);
|
|
27
|
+
if (registeredThread) {
|
|
28
|
+
onAlsoSentToChannelHeaderPress({ targetedMessageId, thread: registeredThread });
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
const parentMessage =
|
|
34
|
+
client.messageStore.get(parentId) ??
|
|
35
|
+
(await channel.getMessagesById([parentId])).messages?.[0];
|
|
36
|
+
|
|
37
|
+
if (!parentMessage) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
onAlsoSentToChannelHeaderPress({
|
|
42
|
+
targetedMessageId,
|
|
43
|
+
thread: client.threads.ensure({ channel, parentMessage }),
|
|
44
|
+
});
|
|
45
|
+
} catch (error) {
|
|
46
|
+
console.error('Error querying parent message:', error);
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
);
|
|
50
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { OwnCapabilitiesContextValue } from '../../../../contexts/ownCapabilitiesContext/OwnCapabilitiesContext';
|
|
2
|
+
import { generateMessage } from '../../../../mock-builders/generator/message';
|
|
3
|
+
import type { MessageActionType } from '../../../MessageMenu/MessageActionListItem';
|
|
4
|
+
import { messageActions, MessageActionsParams } from '../messageActions';
|
|
5
|
+
|
|
6
|
+
const action = (actionType: string) => ({ actionType }) as unknown as MessageActionType;
|
|
7
|
+
|
|
8
|
+
const getActionTypes = (overrides: Partial<MessageActionsParams>) =>
|
|
9
|
+
messageActions({
|
|
10
|
+
banUser: action('banUser'),
|
|
11
|
+
blockUser: action('blockUser'),
|
|
12
|
+
copyMessage: action('copyMessage'),
|
|
13
|
+
deleteMessage: action('deleteMessage'),
|
|
14
|
+
dismissOverlay: jest.fn(),
|
|
15
|
+
editMessage: action('editMessage'),
|
|
16
|
+
error: false,
|
|
17
|
+
flagMessage: action('flagMessage'),
|
|
18
|
+
isMyMessage: false,
|
|
19
|
+
isThreadMessage: false,
|
|
20
|
+
markUnread: action('markUnread'),
|
|
21
|
+
message: generateMessage(),
|
|
22
|
+
muteUser: action('muteUser'),
|
|
23
|
+
ownCapabilities: {
|
|
24
|
+
quoteMessage: true,
|
|
25
|
+
readEvents: true,
|
|
26
|
+
sendReply: true,
|
|
27
|
+
} as OwnCapabilitiesContextValue,
|
|
28
|
+
pinMessage: action('pinMessage'),
|
|
29
|
+
quotedReply: action('quotedReply'),
|
|
30
|
+
retry: action('retry'),
|
|
31
|
+
threadReply: action('threadReply'),
|
|
32
|
+
unpinMessage: action('unpinMessage'),
|
|
33
|
+
updateMessage: jest.fn(),
|
|
34
|
+
...overrides,
|
|
35
|
+
})?.map((item) => item.actionType);
|
|
36
|
+
|
|
37
|
+
describe('messageActions', () => {
|
|
38
|
+
it('offers quoting a thread message, but not replying in a thread or marking it unread', () => {
|
|
39
|
+
const actionTypes = getActionTypes({ isThreadMessage: true });
|
|
40
|
+
|
|
41
|
+
expect(actionTypes).toContain('quotedReply');
|
|
42
|
+
expect(actionTypes).not.toContain('threadReply');
|
|
43
|
+
expect(actionTypes).not.toContain('markUnread');
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('does not offer quoting a message that failed or is an error', () => {
|
|
47
|
+
expect(getActionTypes({ error: true })).not.toContain('quotedReply');
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('does not offer quoting without the quote-message capability', () => {
|
|
51
|
+
expect(
|
|
52
|
+
getActionTypes({
|
|
53
|
+
ownCapabilities: { quoteMessage: false } as OwnCapabilitiesContextValue,
|
|
54
|
+
}),
|
|
55
|
+
).not.toContain('quotedReply');
|
|
56
|
+
});
|
|
57
|
+
});
|
|
@@ -312,8 +312,8 @@ describe('AttachmentUploadPreviewList', () => {
|
|
|
312
312
|
});
|
|
313
313
|
|
|
314
314
|
it('should render FileAttachmentUploadPreview with all failed files', async () => {
|
|
315
|
-
const
|
|
316
|
-
channel.messageComposer.attachmentManager.
|
|
315
|
+
const uploadFileSpy = jest.fn();
|
|
316
|
+
channel.messageComposer.attachmentManager.uploadFile = uploadFileSpy;
|
|
317
317
|
const attachments = [
|
|
318
318
|
generateFileAttachment({
|
|
319
319
|
localMetadata: {
|
|
@@ -353,10 +353,38 @@ describe('AttachmentUploadPreviewList', () => {
|
|
|
353
353
|
await waitFor(() => {
|
|
354
354
|
expect(queryAllByTestId('file-attachment-upload-preview')).toHaveLength(2);
|
|
355
355
|
expect(channel.messageComposer.attachmentManager.attachments).toHaveLength(2);
|
|
356
|
-
expect(
|
|
356
|
+
expect(uploadFileSpy).toHaveBeenCalled();
|
|
357
357
|
});
|
|
358
358
|
});
|
|
359
359
|
|
|
360
|
+
it('does not retry a failed upload while uploads are disabled', async () => {
|
|
361
|
+
const { attachmentManager } = channel.messageComposer;
|
|
362
|
+
const uploadFileSpy = jest.fn();
|
|
363
|
+
attachmentManager.uploadFile = uploadFileSpy;
|
|
364
|
+
jest.spyOn(attachmentManager, 'isUploadEnabled', 'get').mockReturnValue(false);
|
|
365
|
+
|
|
366
|
+
act(() => {
|
|
367
|
+
attachmentManager.upsertAttachments([
|
|
368
|
+
generateFileAttachment({
|
|
369
|
+
localMetadata: { id: 'file-attachment', uploadState: FileState.FAILED },
|
|
370
|
+
}),
|
|
371
|
+
]);
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
renderComponent({ channel, client, props: {} });
|
|
375
|
+
|
|
376
|
+
await waitFor(() => {
|
|
377
|
+
expect(screen.queryAllByTestId('retry-upload-progress-indicator')).toHaveLength(1);
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
act(() => {
|
|
381
|
+
fireEvent.press(screen.getAllByTestId('retry-upload-progress-indicator')[0]);
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
expect(uploadFileSpy).not.toHaveBeenCalled();
|
|
385
|
+
expect(attachmentManager.attachments).toHaveLength(1);
|
|
386
|
+
});
|
|
387
|
+
|
|
360
388
|
it('should render FileAttachmentUploadPreview with all unsupported', async () => {
|
|
361
389
|
const attachments = [
|
|
362
390
|
generateFileAttachment({
|
|
@@ -472,8 +500,8 @@ describe('AttachmentUploadPreviewList', () => {
|
|
|
472
500
|
});
|
|
473
501
|
|
|
474
502
|
it('should render ImageAttachmentUploadPreview with all failed images', async () => {
|
|
475
|
-
const
|
|
476
|
-
channel.messageComposer.attachmentManager.
|
|
503
|
+
const uploadFileSpy = jest.fn();
|
|
504
|
+
channel.messageComposer.attachmentManager.uploadFile = uploadFileSpy;
|
|
477
505
|
const attachments = [
|
|
478
506
|
generateImageAttachment({
|
|
479
507
|
localMetadata: {
|
|
@@ -511,7 +539,7 @@ describe('AttachmentUploadPreviewList', () => {
|
|
|
511
539
|
await waitFor(() => {
|
|
512
540
|
expect(queryAllByTestId('image-attachment-upload-preview')).toHaveLength(1);
|
|
513
541
|
expect(channel.messageComposer.attachmentManager.attachments).toHaveLength(1);
|
|
514
|
-
expect(
|
|
542
|
+
expect(uploadFileSpy).toHaveBeenCalled();
|
|
515
543
|
});
|
|
516
544
|
});
|
|
517
545
|
|
|
@@ -165,9 +165,30 @@ describe('AudioAttachmentUploadPreview render', () => {
|
|
|
165
165
|
});
|
|
166
166
|
});
|
|
167
167
|
|
|
168
|
+
it('should render AudioAttachmentUploadPreview for an attachment without a local file', async () => {
|
|
169
|
+
const attachments = [
|
|
170
|
+
generateAudioAttachment({
|
|
171
|
+
localMetadata: {
|
|
172
|
+
id: 'audio-attachment',
|
|
173
|
+
uploadState: FileState.FINISHED,
|
|
174
|
+
},
|
|
175
|
+
}),
|
|
176
|
+
];
|
|
177
|
+
|
|
178
|
+
act(() => {
|
|
179
|
+
channel.messageComposer.attachmentManager.upsertAttachments(attachments);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
renderComponent({ channel, client, props: {} });
|
|
183
|
+
|
|
184
|
+
await waitFor(() => {
|
|
185
|
+
expect(screen.queryAllByTestId('audio-attachment-upload-preview')).toHaveLength(1);
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
|
|
168
189
|
it('should render AudioAttachmentUploadPreview with all failed files', async () => {
|
|
169
|
-
const
|
|
170
|
-
channel.messageComposer.attachmentManager.
|
|
190
|
+
const uploadFileSpy = jest.fn();
|
|
191
|
+
channel.messageComposer.attachmentManager.uploadFile = uploadFileSpy;
|
|
171
192
|
const attachments = [
|
|
172
193
|
generateAudioAttachment({
|
|
173
194
|
localMetadata: {
|
|
@@ -203,7 +224,7 @@ describe('AudioAttachmentUploadPreview render', () => {
|
|
|
203
224
|
expect(queryAllByTestId('audio-attachment-upload-preview')).toHaveLength(1);
|
|
204
225
|
expect(getByLabelText('audio-attachment-preview')).toBeDefined();
|
|
205
226
|
expect(channel.messageComposer.attachmentManager.attachments).toHaveLength(1);
|
|
206
|
-
expect(
|
|
227
|
+
expect(uploadFileSpy).toHaveBeenCalled();
|
|
207
228
|
});
|
|
208
229
|
});
|
|
209
230
|
|
|
@@ -115,8 +115,7 @@ describe('SendMessageDisallowedIndicator', () => {
|
|
|
115
115
|
expect(queryByTestId('send-message-disallowed-indicator')).toBeNull();
|
|
116
116
|
});
|
|
117
117
|
|
|
118
|
-
// Own capabilities are
|
|
119
|
-
// output-only notification in v10, so drive the change through the store directly.
|
|
118
|
+
// Own capabilities are read from `channel.state`, so the change is driven through the store.
|
|
120
119
|
act(() => {
|
|
121
120
|
channel.state.partialNext({
|
|
122
121
|
ownCapabilities: channel.data!.own_capabilities!.filter(
|
|
@@ -226,12 +225,10 @@ describe("SendMessageDisallowedIndicator's edited state", () => {
|
|
|
226
225
|
});
|
|
227
226
|
|
|
228
227
|
act(() => {
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
own_capabilities: customChannel.data!.own_capabilities!.filter(
|
|
228
|
+
customChannel.state.partialNext({
|
|
229
|
+
ownCapabilities: customChannel.data!.own_capabilities!.filter(
|
|
232
230
|
(capability: string) => capability !== 'send-message',
|
|
233
231
|
),
|
|
234
|
-
type: 'capabilities.changed',
|
|
235
232
|
});
|
|
236
233
|
});
|
|
237
234
|
|
package/src/components/MessageInput/components/AttachmentPreview/AttachmentUploadPreviewList.tsx
CHANGED
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
isVideoAttachment,
|
|
22
22
|
LocalAttachment,
|
|
23
23
|
LocalAudioAttachment,
|
|
24
|
+
LocalUploadAttachment,
|
|
24
25
|
LocalVoiceRecordingAttachment,
|
|
25
26
|
} from 'stream-chat';
|
|
26
27
|
|
|
@@ -292,6 +293,16 @@ const UnMemoizedAttachmentUploadPreviewList = () => {
|
|
|
292
293
|
[attachmentManager, dataRef, prepareForRemoval],
|
|
293
294
|
);
|
|
294
295
|
|
|
296
|
+
// `uploadFile` does not check whether uploads are enabled, so a retry does not start one while
|
|
297
|
+
// they are off.
|
|
298
|
+
const handleRetry = useCallback(
|
|
299
|
+
(attachment: LocalUploadAttachment) => {
|
|
300
|
+
if (!attachmentManager.isUploadEnabled) return;
|
|
301
|
+
return attachmentManager.uploadFile(attachment);
|
|
302
|
+
},
|
|
303
|
+
[attachmentManager],
|
|
304
|
+
);
|
|
305
|
+
|
|
295
306
|
useLayoutEffect(() => {
|
|
296
307
|
const previousData = previousDataRef.current;
|
|
297
308
|
const nextIds = new Set(data.map((attachment) => attachment.localMetadata.id));
|
|
@@ -346,7 +357,7 @@ const UnMemoizedAttachmentUploadPreviewList = () => {
|
|
|
346
357
|
return (
|
|
347
358
|
<ImageAttachmentUploadPreview
|
|
348
359
|
attachment={attachment}
|
|
349
|
-
handleRetry={
|
|
360
|
+
handleRetry={handleRetry}
|
|
350
361
|
removeAttachments={removeAttachments}
|
|
351
362
|
/>
|
|
352
363
|
);
|
|
@@ -354,7 +365,7 @@ const UnMemoizedAttachmentUploadPreviewList = () => {
|
|
|
354
365
|
return (
|
|
355
366
|
<AudioAttachmentUploadPreview
|
|
356
367
|
attachment={attachment}
|
|
357
|
-
handleRetry={
|
|
368
|
+
handleRetry={handleRetry}
|
|
358
369
|
removeAttachments={removeAttachments}
|
|
359
370
|
/>
|
|
360
371
|
);
|
|
@@ -363,7 +374,7 @@ const UnMemoizedAttachmentUploadPreviewList = () => {
|
|
|
363
374
|
return (
|
|
364
375
|
<AudioAttachmentUploadPreview
|
|
365
376
|
attachment={attachment}
|
|
366
|
-
handleRetry={
|
|
377
|
+
handleRetry={handleRetry}
|
|
367
378
|
removeAttachments={removeAttachments}
|
|
368
379
|
/>
|
|
369
380
|
);
|
|
@@ -371,7 +382,7 @@ const UnMemoizedAttachmentUploadPreviewList = () => {
|
|
|
371
382
|
return (
|
|
372
383
|
<FileAttachmentUploadPreview
|
|
373
384
|
attachment={attachment}
|
|
374
|
-
handleRetry={
|
|
385
|
+
handleRetry={handleRetry}
|
|
375
386
|
removeAttachments={removeAttachments}
|
|
376
387
|
/>
|
|
377
388
|
);
|
|
@@ -380,7 +391,7 @@ const UnMemoizedAttachmentUploadPreviewList = () => {
|
|
|
380
391
|
return (
|
|
381
392
|
<VideoAttachmentUploadPreview
|
|
382
393
|
attachment={attachment}
|
|
383
|
-
handleRetry={
|
|
394
|
+
handleRetry={handleRetry}
|
|
384
395
|
removeAttachments={removeAttachments}
|
|
385
396
|
/>
|
|
386
397
|
);
|
|
@@ -388,7 +399,7 @@ const UnMemoizedAttachmentUploadPreviewList = () => {
|
|
|
388
399
|
return (
|
|
389
400
|
<FileAttachmentUploadPreview
|
|
390
401
|
attachment={attachment}
|
|
391
|
-
handleRetry={
|
|
402
|
+
handleRetry={handleRetry}
|
|
392
403
|
removeAttachments={removeAttachments}
|
|
393
404
|
/>
|
|
394
405
|
);
|
|
@@ -399,7 +410,7 @@ const UnMemoizedAttachmentUploadPreviewList = () => {
|
|
|
399
410
|
FileAttachmentUploadPreview,
|
|
400
411
|
ImageAttachmentUploadPreview,
|
|
401
412
|
VideoAttachmentUploadPreview,
|
|
402
|
-
|
|
413
|
+
handleRetry,
|
|
403
414
|
removeAttachments,
|
|
404
415
|
soundPackageAvailable,
|
|
405
416
|
],
|
|
@@ -496,7 +507,7 @@ const UnMemoizedAttachmentUploadPreviewList = () => {
|
|
|
496
507
|
<AttachmentPreviewCell key={attachment.localMetadata.id}>
|
|
497
508
|
<AudioAttachmentUploadPreview
|
|
498
509
|
attachment={attachment}
|
|
499
|
-
handleRetry={
|
|
510
|
+
handleRetry={handleRetry}
|
|
500
511
|
removeAttachments={removeAttachments}
|
|
501
512
|
/>
|
|
502
513
|
</AttachmentPreviewCell>
|
package/src/components/MessageInput/components/AttachmentPreview/AudioAttachmentUploadPreview.tsx
CHANGED
|
@@ -43,11 +43,8 @@ export const AudioAttachmentUploadPreview = ({
|
|
|
43
43
|
const messageComposer = useMessageComposer();
|
|
44
44
|
const isDraft = messageComposer.draftId;
|
|
45
45
|
const isEditing = messageComposer.editedMessage;
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
? attachment.asset_url
|
|
49
|
-
: (attachment.localMetadata.file as FileReference).uri) ??
|
|
50
|
-
(attachment.localMetadata.file as FileReference).uri;
|
|
46
|
+
const fileUri = (attachment.localMetadata.file as FileReference | undefined)?.uri;
|
|
47
|
+
const assetUrl = (isDraft || isEditing ? attachment.asset_url : fileUri) ?? fileUri;
|
|
51
48
|
|
|
52
49
|
const finalAttachment = useMemo(
|
|
53
50
|
() => ({
|
|
@@ -97,8 +97,8 @@ export const useAudioRecorder = ({
|
|
|
97
97
|
if (sendOnComplete) {
|
|
98
98
|
attachmentManager.upsertAttachments([audioFile]);
|
|
99
99
|
sendMessage();
|
|
100
|
-
} else {
|
|
101
|
-
await attachmentManager.
|
|
100
|
+
} else if (attachmentManager.isUploadEnabled) {
|
|
101
|
+
await attachmentManager.uploadFile(audioFile);
|
|
102
102
|
}
|
|
103
103
|
} catch (error) {
|
|
104
104
|
console.log('Error uploading voice recording: ', error);
|
|
@@ -20,8 +20,8 @@ export const InlineUnreadIndicator = (props: InlineUnreadIndicatorProps) => {
|
|
|
20
20
|
{unreadCount
|
|
21
21
|
? t('messageList.newMessages.text', {
|
|
22
22
|
count: unreadCount,
|
|
23
|
-
defaultValue_one: '{{count}}
|
|
24
|
-
defaultValue_other: '{{count}}
|
|
23
|
+
defaultValue_one: '{{count}} unread message',
|
|
24
|
+
defaultValue_other: '{{count}} unread messages',
|
|
25
25
|
})
|
|
26
26
|
: t('messageList.unreadMessages.label', 'Unread Messages')}
|
|
27
27
|
</Text>
|
|
@@ -432,12 +432,8 @@ const MessageListWithContext = (props: MessageListPropsWithContext) => {
|
|
|
432
432
|
}, [disabled]);
|
|
433
433
|
|
|
434
434
|
useEffect(() => {
|
|
435
|
-
if (thread) {
|
|
436
|
-
setAutoscrollToRecent(true);
|
|
437
|
-
return;
|
|
438
|
-
}
|
|
439
|
-
|
|
440
435
|
if (!processedMessageList.length || !paginator) {
|
|
436
|
+
if (thread) setAutoscrollToRecent(true);
|
|
441
437
|
return;
|
|
442
438
|
}
|
|
443
439
|
|
|
@@ -9,8 +9,8 @@ import { InlineUnreadIndicator } from '../InlineUnreadIndicator';
|
|
|
9
9
|
|
|
10
10
|
describe('InlineUnreadIndicator', () => {
|
|
11
11
|
it.each([
|
|
12
|
-
[1, '1
|
|
13
|
-
[3, '3
|
|
12
|
+
[1, '1 unread message'],
|
|
13
|
+
[3, '3 unread messages'],
|
|
14
14
|
])('labels %i unread messages as "%s"', async (unreadCount, label) => {
|
|
15
15
|
const translators = await new Streami18n().init();
|
|
16
16
|
render(
|