stream-chat-react-native-core 10.0.0-rc.4 → 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/hooks/useChannelLifecycle.js +2 -2
- package/lib/commonjs/components/Channel/hooks/useChannelLifecycle.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/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/hooks/useChannelLifecycle.js +2 -2
- package/lib/module/components/Channel/hooks/useChannelLifecycle.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/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/hooks/useChannelLifecycle.d.ts.map +1 -1
- 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/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/__tests__/Channel.test.tsx +40 -7
- package/src/components/Channel/hooks/useChannelLifecycle.ts +8 -4
- package/src/components/Channel/hooks/useCreateOwnCapabilitiesContext.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 +163 -0
- package/src/components/MessageList/hooks/useMessageListFocus.ts +22 -2
- package/src/components/Thread/__tests__/Thread.test.tsx +46 -1
- package/src/components/Thread/components/ThreadFooterComponent.tsx +0 -1
- 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 -37
- package/lib/commonjs/utils/setupCommandUIMiddlewares.js.map +0 -1
- package/lib/module/utils/setupCommandUIMiddlewares.js +0 -37
- 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/__tests__/setupCommandUIMiddlewares.test.ts +0 -49
- package/src/utils/setupCommandUIMiddlewares.ts +0 -34
|
@@ -126,6 +126,47 @@ describe('ChannelPreviewView', () => {
|
|
|
126
126
|
await waitFor(() => screen.queryByText(expectedDisplayName));
|
|
127
127
|
});
|
|
128
128
|
|
|
129
|
+
describe('the sender of the latest message', () => {
|
|
130
|
+
const otherUser = generateUser({ name: 'Count Dooku' });
|
|
131
|
+
|
|
132
|
+
const renderWithLatestMessage = async (latestMessage: ReturnType<typeof generateMessage>) => {
|
|
133
|
+
await initializeChannel(
|
|
134
|
+
generateChannelResponse({
|
|
135
|
+
channel: { custom: { name: 'group' } },
|
|
136
|
+
members: [generateMember({ user: clientUser }), generateMember({ user: otherUser })],
|
|
137
|
+
messages: [latestMessage],
|
|
138
|
+
}),
|
|
139
|
+
);
|
|
140
|
+
render(
|
|
141
|
+
<OverlayProvider accessibility={{ enabled: true }}>
|
|
142
|
+
<Chat client={chatClient}>
|
|
143
|
+
<ChannelPreviewView
|
|
144
|
+
channel={channel as ChannelType}
|
|
145
|
+
lastMessage={latestMessage}
|
|
146
|
+
onSelect={jest.fn()}
|
|
147
|
+
/>
|
|
148
|
+
</Chat>
|
|
149
|
+
</OverlayProvider>,
|
|
150
|
+
);
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
it.each([
|
|
154
|
+
['an own message', clientUser, 'regular', 'You:', 1],
|
|
155
|
+
['an own deleted message', clientUser, 'deleted', 'You:', 0],
|
|
156
|
+
['a deleted message from another member', otherUser, 'deleted', 'Count Dooku:', 0],
|
|
157
|
+
] as const)('names the sender of %s', async (_, user, type, prefix, statusLabels) => {
|
|
158
|
+
await renderWithLatestMessage(generateMessage({ status: 'received', type, user }));
|
|
159
|
+
|
|
160
|
+
// An own message's status hides its sender text from screen readers, so hidden elements count.
|
|
161
|
+
const sender = () =>
|
|
162
|
+
screen.getByTestId('channel-preview-sender', { includeHiddenElements: true });
|
|
163
|
+
await waitFor(() => expect(sender()).toBeTruthy());
|
|
164
|
+
expect(sender().props.children.join('')).toBe(prefix);
|
|
165
|
+
// The delivery status announces itself with "Sent by you" or "Read, sent by you".
|
|
166
|
+
expect(screen.queryAllByLabelText(/sent by you/i)).toHaveLength(statusLabels);
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
|
|
129
170
|
it('should render latest message, truncated to length given by latestMessageLength', async () => {
|
|
130
171
|
const message = generateMessage();
|
|
131
172
|
await initializeChannel(generateChannelResponse());
|
|
@@ -47,7 +47,10 @@ import {
|
|
|
47
47
|
MessageOverlayRuntimeProvider,
|
|
48
48
|
MessageProvider,
|
|
49
49
|
} from '../../contexts/messageContext/MessageContext';
|
|
50
|
-
import {
|
|
50
|
+
import {
|
|
51
|
+
GoToMessage,
|
|
52
|
+
useMessageListItemContext,
|
|
53
|
+
} from '../../contexts/messageListItemContext/MessageListItemContext';
|
|
51
54
|
import {
|
|
52
55
|
MessagesContextValue,
|
|
53
56
|
useMessagesContext,
|
|
@@ -223,7 +226,7 @@ export type MessagePropsWithContext = Pick<
|
|
|
223
226
|
messagesContext: MessagesContextValue;
|
|
224
227
|
/** Whether long pressing a message is enabled. Defaults to true. */
|
|
225
228
|
enableLongPress?: boolean;
|
|
226
|
-
goToMessage?:
|
|
229
|
+
goToMessage?: GoToMessage;
|
|
227
230
|
isTargetedMessage?: boolean;
|
|
228
231
|
/**
|
|
229
232
|
* Called with the message's thread when its replies button is pressed.
|
|
@@ -2,8 +2,6 @@ import React, { useMemo } from 'react';
|
|
|
2
2
|
|
|
3
3
|
import { Pressable, StyleSheet, Text, View } from 'react-native';
|
|
4
4
|
|
|
5
|
-
import { useChannelContext } from '../../../../contexts/channelContext/ChannelContext';
|
|
6
|
-
import { useChatContext } from '../../../../contexts/chatContext/ChatContext';
|
|
7
5
|
import { useComponentsContext } from '../../../../contexts/componentsContext/ComponentsContext';
|
|
8
6
|
import { useMessageContext } from '../../../../contexts/messageContext/MessageContext';
|
|
9
7
|
import { useMessagesContext } from '../../../../contexts/messagesContext/MessagesContext';
|
|
@@ -15,6 +13,7 @@ import {
|
|
|
15
13
|
import { useTranslationContext } from '../../../../contexts/translationContext/TranslationContext';
|
|
16
14
|
import { useStableCallback } from '../../../../hooks';
|
|
17
15
|
import { primitives } from '../../../../theme';
|
|
16
|
+
import { useOpenThreadAtMessage } from '../../hooks/useOpenThreadAtMessage';
|
|
18
17
|
import { useShouldUseOverlayStyles } from '../../hooks/useShouldUseOverlayStyles';
|
|
19
18
|
|
|
20
19
|
type SentToChannelHeaderPropsWithContext = Pick<ThreadContextValue, 'thread'> & {
|
|
@@ -67,10 +66,9 @@ export type SentToChannelHeaderProps = Partial<SentToChannelHeaderPropsWithConte
|
|
|
67
66
|
|
|
68
67
|
export const SentToChannelHeader = (props: SentToChannelHeaderProps) => {
|
|
69
68
|
const { onAlsoSentToChannelHeaderPress } = useMessagesContext();
|
|
70
|
-
const { client } = useChatContext();
|
|
71
|
-
const { channel } = useChannelContext();
|
|
72
69
|
const { thread } = useThreadContext();
|
|
73
70
|
const { message } = useMessageContext();
|
|
71
|
+
const openThreadAtMessage = useOpenThreadAtMessage();
|
|
74
72
|
|
|
75
73
|
const handleOnPress = useStableCallback(async () => {
|
|
76
74
|
if (thread) {
|
|
@@ -78,34 +76,8 @@ export const SentToChannelHeader = (props: SentToChannelHeaderProps) => {
|
|
|
78
76
|
return;
|
|
79
77
|
}
|
|
80
78
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// The parent is only fetched when neither its thread nor the message itself is loaded. Opening
|
|
87
|
-
// the thread loads whatever else it is missing.
|
|
88
|
-
const registeredThread = client.threads.get(parentId);
|
|
89
|
-
if (registeredThread) {
|
|
90
|
-
onAlsoSentToChannelHeaderPress?.({ targetedMessageId: message.id, thread: registeredThread });
|
|
91
|
-
return;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
try {
|
|
95
|
-
const parentMessage =
|
|
96
|
-
client.messageStore.get(parentId) ??
|
|
97
|
-
(await channel.getMessagesById([parentId])).messages?.[0];
|
|
98
|
-
|
|
99
|
-
if (!parentMessage) {
|
|
100
|
-
return;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
onAlsoSentToChannelHeaderPress?.({
|
|
104
|
-
targetedMessageId: message.id,
|
|
105
|
-
thread: client.threads.ensure({ channel, parentMessage }),
|
|
106
|
-
});
|
|
107
|
-
} catch (error) {
|
|
108
|
-
console.error('Error querying parent message:', error);
|
|
79
|
+
if (message.parent_id) {
|
|
80
|
+
await openThreadAtMessage({ parentId: message.parent_id, targetedMessageId: message.id });
|
|
109
81
|
}
|
|
110
82
|
});
|
|
111
83
|
|
|
@@ -254,14 +254,15 @@ const MessageContentWithContext = (props: MessageContentPropsWithContext) => {
|
|
|
254
254
|
|
|
255
255
|
if (onPress) {
|
|
256
256
|
onPress({
|
|
257
|
-
defaultHandler: () =>
|
|
257
|
+
defaultHandler: () =>
|
|
258
|
+
goToMessage(message.quoted_message!.id, message.quoted_message!),
|
|
258
259
|
emitter: 'messageContent',
|
|
259
260
|
event,
|
|
260
261
|
});
|
|
261
262
|
return;
|
|
262
263
|
}
|
|
263
264
|
|
|
264
|
-
goToMessage(message.quoted_message.id);
|
|
265
|
+
goToMessage(message.quoted_message.id, message.quoted_message);
|
|
265
266
|
}}
|
|
266
267
|
onPressIn={(event) => {
|
|
267
268
|
if (onPressIn) {
|
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
import React, { useMemo } from 'react';
|
|
2
2
|
import { StyleSheet, View } from 'react-native';
|
|
3
3
|
|
|
4
|
+
import type { ChannelConfig } from 'stream-chat';
|
|
5
|
+
|
|
4
6
|
import { useA11yLabel } from '../../../a11y/hooks/useA11yLabel';
|
|
7
|
+
import { useChannelContext } from '../../../contexts/channelContext/ChannelContext';
|
|
5
8
|
import { useComponentsContext } from '../../../contexts/componentsContext/ComponentsContext';
|
|
6
9
|
import {
|
|
7
10
|
MessageContextValue,
|
|
8
11
|
useMessageContext,
|
|
9
12
|
} from '../../../contexts/messageContext/MessageContext';
|
|
10
13
|
import { useTheme } from '../../../contexts/themeContext/ThemeContext';
|
|
14
|
+
import { useStateStore } from '../../../hooks/useStateStore';
|
|
11
15
|
import { primitives } from '../../../theme';
|
|
12
16
|
import { MessageStatusTypes } from '../../../utils/utils';
|
|
13
17
|
import { HiddenA11yText } from '../../Accessibility/HiddenA11yText';
|
|
@@ -15,15 +19,27 @@ import { useIsMessageDeliveredToOthers } from '../hooks/useIsMessageDeliveredToO
|
|
|
15
19
|
import { useIsMessageReadByOthers } from '../hooks/useIsMessageReadByOthers';
|
|
16
20
|
import { useShouldUseOverlayStyles } from '../hooks/useShouldUseOverlayStyles';
|
|
17
21
|
|
|
22
|
+
const readEventsSelector = ({ readEvents }: ChannelConfig) => ({
|
|
23
|
+
readEventsEnabled: readEvents.enabled,
|
|
24
|
+
});
|
|
25
|
+
|
|
18
26
|
export type MessageStatusPropsWithContext = Pick<MessageContextValue, 'message'> & {
|
|
19
27
|
/** Whether the message reached at least one other member. */
|
|
20
28
|
delivered: boolean;
|
|
21
29
|
/** Whether at least one other member read the message. */
|
|
22
30
|
read: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Whether the channel has read events. Without them only the sending status shows, as in the
|
|
33
|
+
* channel preview.
|
|
34
|
+
* @default true
|
|
35
|
+
*/
|
|
36
|
+
readEventsEnabled?: boolean;
|
|
23
37
|
};
|
|
24
38
|
|
|
25
39
|
const MessageStatusWithContext = (props: MessageStatusPropsWithContext) => {
|
|
26
|
-
const {
|
|
40
|
+
const { message, readEventsEnabled = true } = props;
|
|
41
|
+
const delivered = readEventsEnabled && props.delivered;
|
|
42
|
+
const read = readEventsEnabled && props.read;
|
|
27
43
|
|
|
28
44
|
const styles = useStyles();
|
|
29
45
|
const { icons } = useComponentsContext();
|
|
@@ -38,6 +54,7 @@ const MessageStatusWithContext = (props: MessageStatusPropsWithContext) => {
|
|
|
38
54
|
|
|
39
55
|
const sending = message.status === MessageStatusTypes.SENDING;
|
|
40
56
|
const sent =
|
|
57
|
+
readEventsEnabled &&
|
|
41
58
|
message.status === MessageStatusTypes.RECEIVED &&
|
|
42
59
|
!delivered &&
|
|
43
60
|
!read &&
|
|
@@ -55,7 +72,11 @@ const MessageStatusWithContext = (props: MessageStatusPropsWithContext) => {
|
|
|
55
72
|
: '',
|
|
56
73
|
);
|
|
57
74
|
|
|
58
|
-
if (
|
|
75
|
+
if (
|
|
76
|
+
message.status === MessageStatusTypes.FAILED ||
|
|
77
|
+
message.type === 'error' ||
|
|
78
|
+
message.type === 'deleted'
|
|
79
|
+
) {
|
|
59
80
|
return null;
|
|
60
81
|
}
|
|
61
82
|
|
|
@@ -109,8 +130,23 @@ const areEqual = (
|
|
|
109
130
|
prevProps: MessageStatusPropsWithContext,
|
|
110
131
|
nextProps: MessageStatusPropsWithContext,
|
|
111
132
|
) => {
|
|
112
|
-
const {
|
|
113
|
-
|
|
133
|
+
const {
|
|
134
|
+
delivered: prevDelivered,
|
|
135
|
+
message: prevMessage,
|
|
136
|
+
read: prevRead,
|
|
137
|
+
readEventsEnabled: prevReadEventsEnabled,
|
|
138
|
+
} = prevProps;
|
|
139
|
+
const {
|
|
140
|
+
delivered: nextDelivered,
|
|
141
|
+
message: nextMessage,
|
|
142
|
+
read: nextRead,
|
|
143
|
+
readEventsEnabled: nextReadEventsEnabled,
|
|
144
|
+
} = nextProps;
|
|
145
|
+
|
|
146
|
+
const readEventsEnabledEqual = prevReadEventsEnabled === nextReadEventsEnabled;
|
|
147
|
+
if (!readEventsEnabledEqual) {
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
114
150
|
|
|
115
151
|
const deliveredEqual = prevDelivered === nextDelivered;
|
|
116
152
|
if (!deliveredEqual) {
|
|
@@ -141,14 +177,17 @@ export type MessageStatusProps = Partial<MessageStatusPropsWithContext>;
|
|
|
141
177
|
export const MessageStatus = (props: MessageStatusProps) => {
|
|
142
178
|
const { message: contextMessage } = useMessageContext();
|
|
143
179
|
const message = props.message ?? contextMessage;
|
|
180
|
+
const { channel } = useChannelContext();
|
|
144
181
|
const readByOthers = useIsMessageReadByOthers({ message });
|
|
145
182
|
const deliveredToOthers = useIsMessageDeliveredToOthers({ message });
|
|
183
|
+
const { readEventsEnabled } = useStateStore(channel?.configState, readEventsSelector) ?? {};
|
|
146
184
|
|
|
147
185
|
return (
|
|
148
186
|
<MemoizedMessageStatus
|
|
149
187
|
delivered={props.delivered ?? deliveredToOthers}
|
|
150
188
|
message={message}
|
|
151
189
|
read={props.read ?? readByOthers}
|
|
190
|
+
readEventsEnabled={props.readEventsEnabled ?? readEventsEnabled}
|
|
152
191
|
/>
|
|
153
192
|
);
|
|
154
193
|
};
|
|
@@ -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
|
|