stream-chat-react-native-core 4.8.0-beta.1 → 4.9.0-beta.2
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/Attachment/GallaryImage.js +41 -0
- package/lib/commonjs/components/Attachment/GallaryImage.js.map +1 -0
- package/lib/commonjs/components/Attachment/Gallery.js +101 -36
- package/lib/commonjs/components/Attachment/Gallery.js.map +1 -1
- package/lib/commonjs/components/Attachment/Giphy.js +118 -25
- package/lib/commonjs/components/Attachment/Giphy.js.map +1 -1
- package/lib/commonjs/components/Attachment/ImageLoadingFailedIndicator.js +105 -0
- package/lib/commonjs/components/Attachment/ImageLoadingFailedIndicator.js.map +1 -0
- package/lib/commonjs/components/Attachment/ImageLoadingIndicator.js +58 -0
- package/lib/commonjs/components/Attachment/ImageLoadingIndicator.js.map +1 -0
- package/lib/commonjs/components/Attachment/hooks/useLoadingImage.js +32 -0
- package/lib/commonjs/components/Attachment/hooks/useLoadingImage.js.map +1 -0
- package/lib/commonjs/components/AutoCompleteInput/AutoCompleteInput.js +3 -2
- package/lib/commonjs/components/AutoCompleteInput/AutoCompleteInput.js.map +1 -1
- package/lib/commonjs/components/AutoCompleteInput/AutoCompleteSuggestionItem.js +1 -1
- package/lib/commonjs/components/AutoCompleteInput/AutoCompleteSuggestionItem.js.map +1 -1
- package/lib/commonjs/components/Channel/Channel.js +23 -13
- package/lib/commonjs/components/Channel/Channel.js.map +1 -1
- package/lib/commonjs/components/Channel/hooks/useCreateMessagesContext.js +4 -0
- package/lib/commonjs/components/Channel/hooks/useCreateMessagesContext.js.map +1 -1
- package/lib/commonjs/components/ChannelPreview/hooks/useLatestMessagePreview.js +43 -19
- package/lib/commonjs/components/ChannelPreview/hooks/useLatestMessagePreview.js.map +1 -1
- package/lib/commonjs/components/MessageInput/FileUploadPreview.js +20 -19
- package/lib/commonjs/components/MessageInput/FileUploadPreview.js.map +1 -1
- package/lib/commonjs/components/MessageInput/ImageUploadPreview.js +14 -15
- package/lib/commonjs/components/MessageInput/ImageUploadPreview.js.map +1 -1
- package/lib/commonjs/contexts/messagesContext/MessagesContext.js +2 -2
- package/lib/commonjs/contexts/messagesContext/MessagesContext.js.map +1 -1
- package/lib/commonjs/contexts/themeContext/utils/theme.js +4 -0
- package/lib/commonjs/contexts/themeContext/utils/theme.js.map +1 -1
- package/lib/commonjs/mock-builders/api/channelMocks.js +299 -0
- package/lib/commonjs/mock-builders/api/channelMocks.js.map +1 -0
- package/lib/commonjs/version.json +1 -1
- package/lib/module/components/Attachment/GallaryImage.js +41 -0
- package/lib/module/components/Attachment/GallaryImage.js.map +1 -0
- package/lib/module/components/Attachment/Gallery.js +101 -36
- package/lib/module/components/Attachment/Gallery.js.map +1 -1
- package/lib/module/components/Attachment/Giphy.js +118 -25
- package/lib/module/components/Attachment/Giphy.js.map +1 -1
- package/lib/module/components/Attachment/ImageLoadingFailedIndicator.js +105 -0
- package/lib/module/components/Attachment/ImageLoadingFailedIndicator.js.map +1 -0
- package/lib/module/components/Attachment/ImageLoadingIndicator.js +58 -0
- package/lib/module/components/Attachment/ImageLoadingIndicator.js.map +1 -0
- package/lib/module/components/Attachment/hooks/useLoadingImage.js +32 -0
- package/lib/module/components/Attachment/hooks/useLoadingImage.js.map +1 -0
- package/lib/module/components/AutoCompleteInput/AutoCompleteInput.js +3 -2
- package/lib/module/components/AutoCompleteInput/AutoCompleteInput.js.map +1 -1
- package/lib/module/components/AutoCompleteInput/AutoCompleteSuggestionItem.js +1 -1
- package/lib/module/components/AutoCompleteInput/AutoCompleteSuggestionItem.js.map +1 -1
- package/lib/module/components/Channel/Channel.js +23 -13
- package/lib/module/components/Channel/Channel.js.map +1 -1
- package/lib/module/components/Channel/hooks/useCreateMessagesContext.js +4 -0
- package/lib/module/components/Channel/hooks/useCreateMessagesContext.js.map +1 -1
- package/lib/module/components/ChannelPreview/hooks/useLatestMessagePreview.js +43 -19
- package/lib/module/components/ChannelPreview/hooks/useLatestMessagePreview.js.map +1 -1
- package/lib/module/components/MessageInput/FileUploadPreview.js +20 -19
- package/lib/module/components/MessageInput/FileUploadPreview.js.map +1 -1
- package/lib/module/components/MessageInput/ImageUploadPreview.js +14 -15
- package/lib/module/components/MessageInput/ImageUploadPreview.js.map +1 -1
- package/lib/module/contexts/messagesContext/MessagesContext.js +2 -2
- package/lib/module/contexts/messagesContext/MessagesContext.js.map +1 -1
- package/lib/module/contexts/themeContext/utils/theme.js +4 -0
- package/lib/module/contexts/themeContext/utils/theme.js.map +1 -1
- package/lib/module/mock-builders/api/channelMocks.js +299 -0
- package/lib/module/mock-builders/api/channelMocks.js.map +1 -0
- package/lib/module/version.json +1 -1
- package/lib/typescript/components/Attachment/GallaryImage.d.ts +5 -0
- package/lib/typescript/components/Attachment/Gallery.d.ts +1 -1
- package/lib/typescript/components/Attachment/Giphy.d.ts +1 -1
- package/lib/typescript/components/Attachment/ImageLoadingFailedIndicator.d.ts +3 -0
- package/lib/typescript/components/Attachment/ImageLoadingIndicator.d.ts +3 -0
- package/lib/typescript/components/Attachment/hooks/useLoadingImage.d.ts +7 -0
- package/lib/typescript/components/Channel/Channel.d.ts +1 -1
- package/lib/typescript/components/Channel/hooks/useCreateMessagesContext.d.ts +1 -1
- package/lib/typescript/components/ChannelPreview/hooks/__tests__/useLatestMessagePreview.test.d.ts +1 -0
- package/lib/typescript/contexts/messagesContext/MessagesContext.d.ts +10 -0
- package/lib/typescript/contexts/themeContext/utils/theme.d.ts +4 -0
- package/lib/typescript/mock-builders/api/channelMocks.d.ts +14 -0
- package/package.json +1 -1
- package/src/components/Attachment/GallaryImage.tsx +22 -0
- package/src/components/Attachment/Gallery.tsx +68 -35
- package/src/components/Attachment/Giphy.tsx +68 -1
- package/src/components/Attachment/ImageLoadingFailedIndicator.tsx +56 -0
- package/src/components/Attachment/ImageLoadingIndicator.tsx +31 -0
- package/src/components/Attachment/__tests__/Attachment.test.js +9 -3
- package/src/components/Attachment/__tests__/Gallery.test.js +36 -1
- package/src/components/Attachment/__tests__/Giphy.test.js +110 -90
- package/src/components/Attachment/hooks/useLoadingImage.tsx +8 -0
- package/src/components/AutoCompleteInput/AutoCompleteInput.tsx +2 -1
- package/src/components/AutoCompleteInput/AutoCompleteSuggestionItem.tsx +1 -1
- package/src/components/Channel/Channel.tsx +8 -0
- package/src/components/Channel/hooks/useCreateMessagesContext.ts +4 -0
- package/src/components/ChannelPreview/__tests__/ChannelPreviewMessenger.test.js +0 -1
- package/src/components/ChannelPreview/hooks/__tests__/useLatestMessagePreview.test.tsx +145 -0
- package/src/components/ChannelPreview/hooks/useLatestMessagePreview.ts +50 -24
- package/src/components/MessageInput/FileUploadPreview.tsx +5 -4
- package/src/components/MessageInput/ImageUploadPreview.tsx +2 -3
- package/src/components/MessageInput/__tests__/__snapshots__/MessageInput.test.js.snap +1 -0
- package/src/components/Thread/__tests__/__snapshots__/Thread.test.js.snap +1 -0
- package/src/contexts/messagesContext/MessagesContext.tsx +15 -0
- package/src/contexts/themeContext/utils/theme.ts +8 -0
- package/src/mock-builders/api/channelMocks.tsx +272 -0
- package/src/version.json +1 -1
|
@@ -1,22 +1,28 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
cleanup,
|
|
5
|
+
fireEvent,
|
|
6
|
+
render,
|
|
7
|
+
waitFor,
|
|
8
|
+
waitForElementToBeRemoved,
|
|
9
|
+
} from '@testing-library/react-native';
|
|
4
10
|
|
|
5
11
|
import { MessageProvider } from '../../../contexts/messageContext/MessageContext';
|
|
12
|
+
import { MessagesProvider } from '../../../contexts/messagesContext/MessagesContext';
|
|
6
13
|
import { OverlayProvider } from '../../../contexts/overlayContext/OverlayProvider';
|
|
7
14
|
|
|
8
15
|
import { ThemeProvider } from '../../../contexts/themeContext/ThemeContext';
|
|
9
16
|
import { getOrCreateChannelApi } from '../../../mock-builders/api/getOrCreateChannel';
|
|
10
17
|
import { useMockedApis } from '../../../mock-builders/api/useMockedApis';
|
|
11
|
-
import {
|
|
12
|
-
generateGiphyAttachment,
|
|
13
|
-
generateImgurAttachment,
|
|
14
|
-
} from '../../../mock-builders/generator/attachment';
|
|
18
|
+
import { generateGiphyAttachment } from '../../../mock-builders/generator/attachment';
|
|
15
19
|
import { generateChannelResponse } from '../../../mock-builders/generator/channel';
|
|
16
20
|
import { generateMember } from '../../../mock-builders/generator/member';
|
|
17
21
|
import { generateMessage } from '../../../mock-builders/generator/message';
|
|
18
22
|
import { generateUser } from '../../../mock-builders/generator/user';
|
|
19
23
|
import { getTestClientWithUser } from '../../../mock-builders/mock';
|
|
24
|
+
import { ImageLoadingFailedIndicator } from '../../Attachment/ImageLoadingFailedIndicator';
|
|
25
|
+
import { ImageLoadingIndicator } from '../../Attachment/ImageLoadingIndicator';
|
|
20
26
|
import { Channel } from '../../Channel/Channel';
|
|
21
27
|
import { Chat } from '../../Chat/Chat';
|
|
22
28
|
import { MessageList } from '../../MessageList/MessageList';
|
|
@@ -27,15 +33,66 @@ describe('Giphy', () => {
|
|
|
27
33
|
const message = generateMessage();
|
|
28
34
|
return (
|
|
29
35
|
<ThemeProvider>
|
|
30
|
-
<
|
|
31
|
-
<
|
|
32
|
-
|
|
36
|
+
<MessagesProvider value={{ ImageLoadingFailedIndicator, ImageLoadingIndicator }}>
|
|
37
|
+
<MessageProvider value={{ message }}>
|
|
38
|
+
<Giphy {...props} />
|
|
39
|
+
</MessageProvider>
|
|
40
|
+
</MessagesProvider>
|
|
33
41
|
</ThemeProvider>
|
|
34
42
|
);
|
|
35
43
|
};
|
|
44
|
+
let chatClient;
|
|
45
|
+
let channel;
|
|
46
|
+
let attachment;
|
|
47
|
+
|
|
48
|
+
const actions = [
|
|
49
|
+
{ name: 'image_action', text: 'Send', value: 'send' },
|
|
50
|
+
{ name: 'image_action', text: 'Shuffle', value: 'shuffle' },
|
|
51
|
+
{
|
|
52
|
+
name: 'image_action',
|
|
53
|
+
text: 'Cancel',
|
|
54
|
+
value: 'cancel',
|
|
55
|
+
},
|
|
56
|
+
];
|
|
57
|
+
|
|
58
|
+
const giphy = {
|
|
59
|
+
fixed_height: {
|
|
60
|
+
height: '200',
|
|
61
|
+
url: 'https://media1.giphy.com/media/test/fixed_height.gif',
|
|
62
|
+
width: '375',
|
|
63
|
+
},
|
|
64
|
+
original: {
|
|
65
|
+
height: '256',
|
|
66
|
+
url: 'https://media1.giphy.com/media/test/original.gif',
|
|
67
|
+
width: '480',
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
const initChannel = async () => {
|
|
71
|
+
const user1 = generateUser();
|
|
72
|
+
attachment = generateGiphyAttachment();
|
|
73
|
+
|
|
74
|
+
const mockedChannel = generateChannelResponse({
|
|
75
|
+
members: [generateMember({ user: user1 })],
|
|
76
|
+
messages: [
|
|
77
|
+
generateMessage({ user: user1 }),
|
|
78
|
+
generateMessage({ type: 'system', user: undefined }),
|
|
79
|
+
generateMessage({ attachments: [{ ...attachment }], user: user1 }),
|
|
80
|
+
],
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
chatClient = await getTestClientWithUser({ id: 'testID' });
|
|
84
|
+
useMockedApis(chatClient, [getOrCreateChannelApi(mockedChannel)]);
|
|
85
|
+
channel = chatClient.channel('messaging', mockedChannel.id);
|
|
86
|
+
await channel.watch();
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
beforeEach(async () => {
|
|
90
|
+
await initChannel();
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
afterEach(cleanup);
|
|
36
94
|
|
|
37
95
|
it('should render Card component for "imgur" type attachment', async () => {
|
|
38
|
-
const attachment = generateImgurAttachment();
|
|
39
96
|
const { getByTestId } = render(getAttachmentComponent({ attachment }));
|
|
40
97
|
|
|
41
98
|
await waitFor(() => {
|
|
@@ -44,7 +101,6 @@ describe('Giphy', () => {
|
|
|
44
101
|
});
|
|
45
102
|
|
|
46
103
|
it('should render Card component for "giphy" type attachment', async () => {
|
|
47
|
-
const attachment = generateGiphyAttachment();
|
|
48
104
|
const { getByTestId } = render(getAttachmentComponent({ attachment }));
|
|
49
105
|
|
|
50
106
|
await waitFor(() => {
|
|
@@ -53,19 +109,7 @@ describe('Giphy', () => {
|
|
|
53
109
|
});
|
|
54
110
|
|
|
55
111
|
it('"giphy" attachment size should be customisable', async () => {
|
|
56
|
-
|
|
57
|
-
attachment.giphy = {
|
|
58
|
-
fixed_height: {
|
|
59
|
-
height: '200',
|
|
60
|
-
url: 'https://media1.giphy.com/media/test/fixed_height.gif',
|
|
61
|
-
width: '375',
|
|
62
|
-
},
|
|
63
|
-
original: {
|
|
64
|
-
height: '256',
|
|
65
|
-
url: 'https://media1.giphy.com/media/test/original.gif',
|
|
66
|
-
width: '480',
|
|
67
|
-
},
|
|
68
|
-
};
|
|
112
|
+
attachment.giphy = giphy;
|
|
69
113
|
const { getByTestId: getByTestIdFixedHeight } = render(
|
|
70
114
|
getAttachmentComponent({ attachment, giphyVersion: 'fixed_height' }),
|
|
71
115
|
);
|
|
@@ -94,16 +138,7 @@ describe('Giphy', () => {
|
|
|
94
138
|
});
|
|
95
139
|
|
|
96
140
|
it('show render giphy action UI and all the 3 action buttons', async () => {
|
|
97
|
-
|
|
98
|
-
attachment.actions = [
|
|
99
|
-
{ name: 'image_action', text: 'Send', value: 'send' },
|
|
100
|
-
{ name: 'image_action', text: 'Shuffle', value: 'shuffle' },
|
|
101
|
-
{
|
|
102
|
-
name: 'image_action',
|
|
103
|
-
text: 'Cancel',
|
|
104
|
-
value: 'cancel',
|
|
105
|
-
},
|
|
106
|
-
];
|
|
141
|
+
attachment.actions = actions;
|
|
107
142
|
const { getByTestId } = render(
|
|
108
143
|
getAttachmentComponent({ attachment, giphyVersion: 'fixed_height' }),
|
|
109
144
|
);
|
|
@@ -117,17 +152,8 @@ describe('Giphy', () => {
|
|
|
117
152
|
});
|
|
118
153
|
|
|
119
154
|
it('should trigger the cancel giphy action', async () => {
|
|
120
|
-
const attachment = generateGiphyAttachment();
|
|
121
155
|
const handleAction = jest.fn();
|
|
122
|
-
attachment.actions =
|
|
123
|
-
{ name: 'image_action', text: 'Send', value: 'send' },
|
|
124
|
-
{ name: 'image_action', text: 'Shuffle', value: 'shuffle' },
|
|
125
|
-
{
|
|
126
|
-
name: 'image_action',
|
|
127
|
-
text: 'Cancel',
|
|
128
|
-
value: 'cancel',
|
|
129
|
-
},
|
|
130
|
-
];
|
|
156
|
+
attachment.actions = actions;
|
|
131
157
|
const { getByTestId } = render(
|
|
132
158
|
getAttachmentComponent({
|
|
133
159
|
attachment,
|
|
@@ -150,17 +176,8 @@ describe('Giphy', () => {
|
|
|
150
176
|
});
|
|
151
177
|
|
|
152
178
|
it('should trigger the shuffle giphy action', async () => {
|
|
153
|
-
const attachment = generateGiphyAttachment();
|
|
154
179
|
const handleAction = jest.fn();
|
|
155
|
-
attachment.actions =
|
|
156
|
-
{ name: 'image_action', text: 'Send', value: 'send' },
|
|
157
|
-
{ name: 'image_action', text: 'Shuffle', value: 'shuffle' },
|
|
158
|
-
{
|
|
159
|
-
name: 'image_action',
|
|
160
|
-
text: 'Cancel',
|
|
161
|
-
value: 'cancel',
|
|
162
|
-
},
|
|
163
|
-
];
|
|
180
|
+
attachment.actions = actions;
|
|
164
181
|
const { getByTestId } = render(
|
|
165
182
|
getAttachmentComponent({
|
|
166
183
|
attachment,
|
|
@@ -183,17 +200,8 @@ describe('Giphy', () => {
|
|
|
183
200
|
});
|
|
184
201
|
|
|
185
202
|
it('should trigger the send giphy action', async () => {
|
|
186
|
-
const attachment = generateGiphyAttachment();
|
|
187
203
|
const handleAction = jest.fn();
|
|
188
|
-
attachment.actions =
|
|
189
|
-
{ name: 'image_action', text: 'Send', value: 'send' },
|
|
190
|
-
{ name: 'image_action', text: 'Shuffle', value: 'shuffle' },
|
|
191
|
-
{
|
|
192
|
-
name: 'image_action',
|
|
193
|
-
text: 'Cancel',
|
|
194
|
-
value: 'cancel',
|
|
195
|
-
},
|
|
196
|
-
];
|
|
204
|
+
attachment.actions = actions;
|
|
197
205
|
const { getByTestId } = render(
|
|
198
206
|
getAttachmentComponent({
|
|
199
207
|
attachment,
|
|
@@ -217,16 +225,7 @@ describe('Giphy', () => {
|
|
|
217
225
|
|
|
218
226
|
it('giphy attachment UI should render within the message list with actions', async () => {
|
|
219
227
|
const user1 = generateUser();
|
|
220
|
-
|
|
221
|
-
attachment.actions = [
|
|
222
|
-
{ name: 'image_action', text: 'Send', value: 'send' },
|
|
223
|
-
{ name: 'image_action', text: 'Shuffle', value: 'shuffle' },
|
|
224
|
-
{
|
|
225
|
-
name: 'image_action',
|
|
226
|
-
text: 'Cancel',
|
|
227
|
-
value: 'cancel',
|
|
228
|
-
},
|
|
229
|
-
];
|
|
228
|
+
attachment.actions = actions;
|
|
230
229
|
const mockedChannel = generateChannelResponse({
|
|
231
230
|
members: [generateMember({ user: user1 })],
|
|
232
231
|
messages: [
|
|
@@ -260,23 +259,6 @@ describe('Giphy', () => {
|
|
|
260
259
|
});
|
|
261
260
|
|
|
262
261
|
it('giphy attachment UI should render within the message list', async () => {
|
|
263
|
-
const user1 = generateUser();
|
|
264
|
-
const attachment = generateGiphyAttachment();
|
|
265
|
-
|
|
266
|
-
const mockedChannel = generateChannelResponse({
|
|
267
|
-
members: [generateMember({ user: user1 })],
|
|
268
|
-
messages: [
|
|
269
|
-
generateMessage({ user: user1 }),
|
|
270
|
-
generateMessage({ type: 'system', user: undefined }),
|
|
271
|
-
generateMessage({ attachments: [{ ...attachment }], user: user1 }),
|
|
272
|
-
],
|
|
273
|
-
});
|
|
274
|
-
|
|
275
|
-
const chatClient = await getTestClientWithUser({ id: 'testID' });
|
|
276
|
-
useMockedApis(chatClient, [getOrCreateChannelApi(mockedChannel)]);
|
|
277
|
-
const channel = chatClient.channel('messaging', mockedChannel.id);
|
|
278
|
-
await channel.watch();
|
|
279
|
-
|
|
280
262
|
const { queryByTestId } = render(
|
|
281
263
|
<OverlayProvider>
|
|
282
264
|
<Chat client={chatClient}>
|
|
@@ -291,4 +273,42 @@ describe('Giphy', () => {
|
|
|
291
273
|
expect(queryByTestId('giphy-attachment')).toBeTruthy();
|
|
292
274
|
});
|
|
293
275
|
});
|
|
276
|
+
|
|
277
|
+
it('should render a error indicator in giphy image', () => {
|
|
278
|
+
const { getByA11yLabel, getByAccessibilityHint } = render(
|
|
279
|
+
<OverlayProvider>
|
|
280
|
+
<Chat client={chatClient}>
|
|
281
|
+
<Channel channel={channel}>
|
|
282
|
+
<MessageList />
|
|
283
|
+
</Channel>
|
|
284
|
+
</Chat>
|
|
285
|
+
</OverlayProvider>,
|
|
286
|
+
);
|
|
287
|
+
|
|
288
|
+
fireEvent(getByA11yLabel('giphy-attachment-image'), 'error');
|
|
289
|
+
expect(getByAccessibilityHint('image-loading-error')).toBeTruthy();
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
it('should render a loading indicator in giphy image and when successful render the image', () => {
|
|
293
|
+
const { getByA11yLabel, getByAccessibilityHint } = render(
|
|
294
|
+
<OverlayProvider>
|
|
295
|
+
<Chat client={chatClient}>
|
|
296
|
+
<Channel channel={channel}>
|
|
297
|
+
<MessageList />
|
|
298
|
+
</Channel>
|
|
299
|
+
</Chat>
|
|
300
|
+
</OverlayProvider>,
|
|
301
|
+
);
|
|
302
|
+
|
|
303
|
+
expect(getByAccessibilityHint('image-loading')).toBeTruthy();
|
|
304
|
+
|
|
305
|
+
fireEvent(getByA11yLabel('giphy-attachment-image'), 'onLoadStart');
|
|
306
|
+
|
|
307
|
+
expect(getByAccessibilityHint('image-loading')).toBeTruthy();
|
|
308
|
+
|
|
309
|
+
fireEvent(getByA11yLabel('giphy-attachment-image'), 'onLoadFinish');
|
|
310
|
+
|
|
311
|
+
waitForElementToBeRemoved(() => getByAccessibilityHint('image-loading'));
|
|
312
|
+
expect(getByA11yLabel('giphy-attachment-image')).toBeTruthy();
|
|
313
|
+
});
|
|
294
314
|
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
|
|
3
|
+
export const useLoadingImage = () => {
|
|
4
|
+
const [isLoadingImage, setLoadingImage] = useState(true);
|
|
5
|
+
const [isLoadingImageError, setLoadingImageError] = useState(false);
|
|
6
|
+
|
|
7
|
+
return { isLoadingImage, isLoadingImageError, setLoadingImage, setLoadingImageError };
|
|
8
|
+
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { useEffect, useRef, useState } from 'react';
|
|
2
|
-
import { StyleSheet, TextInput, TextInputProps } from 'react-native';
|
|
2
|
+
import { I18nManager, StyleSheet, TextInput, TextInputProps } from 'react-native';
|
|
3
3
|
|
|
4
4
|
import throttle from 'lodash/throttle';
|
|
5
5
|
|
|
@@ -417,6 +417,7 @@ const AutoCompleteInputWithContext = <
|
|
|
417
417
|
{
|
|
418
418
|
color: black,
|
|
419
419
|
maxHeight: (textHeight || 17) * numberOfLines,
|
|
420
|
+
textAlign: I18nManager.isRTL ? 'right' : 'left',
|
|
420
421
|
},
|
|
421
422
|
inputBox,
|
|
422
423
|
]}
|
|
@@ -82,6 +82,8 @@ import { FileAttachmentGroup as FileAttachmentGroupDefault } from '../Attachment
|
|
|
82
82
|
import { FileIcon as FileIconDefault } from '../Attachment/FileIcon';
|
|
83
83
|
import { Gallery as GalleryDefault } from '../Attachment/Gallery';
|
|
84
84
|
import { Giphy as GiphyDefault } from '../Attachment/Giphy';
|
|
85
|
+
import { ImageLoadingFailedIndicator as ImageLoadingFailedIndicatorDefault } from '../Attachment/ImageLoadingFailedIndicator';
|
|
86
|
+
import { ImageLoadingIndicator as ImageLoadingIndicatorDefault } from '../Attachment/ImageLoadingIndicator';
|
|
85
87
|
import { VideoThumbnail as VideoThumbnailDefault } from '../Attachment/VideoThumbnail';
|
|
86
88
|
import { AutoCompleteSuggestionHeader as AutoCompleteSuggestionHeaderDefault } from '../AutoCompleteInput/AutoCompleteSuggestionHeader';
|
|
87
89
|
import { AutoCompleteSuggestionItem as AutoCompleteSuggestionItemDefault } from '../AutoCompleteInput/AutoCompleteSuggestionItem';
|
|
@@ -258,6 +260,8 @@ export type ChannelPropsWithContext<
|
|
|
258
260
|
| 'InlineUnreadIndicator'
|
|
259
261
|
| 'isAttachmentEqual'
|
|
260
262
|
| 'legacyImageViewerSwipeBehaviour'
|
|
263
|
+
| 'ImageLoadingFailedIndicator'
|
|
264
|
+
| 'ImageLoadingIndicator'
|
|
261
265
|
| 'markdownRules'
|
|
262
266
|
| 'Message'
|
|
263
267
|
| 'messageActions'
|
|
@@ -470,6 +474,8 @@ const ChannelWithContext = <
|
|
|
470
474
|
LoadingIndicator = LoadingIndicatorDefault,
|
|
471
475
|
loadingMore: loadingMoreProp,
|
|
472
476
|
loadingMoreRecent: loadingMoreRecentProp,
|
|
477
|
+
ImageLoadingFailedIndicator = ImageLoadingFailedIndicatorDefault,
|
|
478
|
+
ImageLoadingIndicator = ImageLoadingIndicatorDefault,
|
|
473
479
|
markdownRules,
|
|
474
480
|
maxMessageLength: maxMessageLengthProp,
|
|
475
481
|
maxNumberOfFiles = 10,
|
|
@@ -1700,6 +1706,8 @@ const ChannelWithContext = <
|
|
|
1700
1706
|
handleReaction,
|
|
1701
1707
|
handleRetry,
|
|
1702
1708
|
handleThreadReply,
|
|
1709
|
+
ImageLoadingFailedIndicator,
|
|
1710
|
+
ImageLoadingIndicator,
|
|
1703
1711
|
initialScrollToFirstUnreadMessage,
|
|
1704
1712
|
InlineDateSeparator,
|
|
1705
1713
|
InlineUnreadIndicator,
|
|
@@ -39,6 +39,8 @@ export const useCreateMessagesContext = <
|
|
|
39
39
|
handleReaction,
|
|
40
40
|
handleRetry,
|
|
41
41
|
handleThreadReply,
|
|
42
|
+
ImageLoadingFailedIndicator,
|
|
43
|
+
ImageLoadingIndicator,
|
|
42
44
|
initialScrollToFirstUnreadMessage,
|
|
43
45
|
InlineDateSeparator,
|
|
44
46
|
InlineUnreadIndicator,
|
|
@@ -126,6 +128,8 @@ export const useCreateMessagesContext = <
|
|
|
126
128
|
handleReaction,
|
|
127
129
|
handleRetry,
|
|
128
130
|
handleThreadReply,
|
|
131
|
+
ImageLoadingFailedIndicator,
|
|
132
|
+
ImageLoadingIndicator,
|
|
129
133
|
initialScrollToFirstUnreadMessage,
|
|
130
134
|
InlineDateSeparator,
|
|
131
135
|
InlineUnreadIndicator,
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import React, { FC } from 'react';
|
|
2
|
+
|
|
3
|
+
import { renderHook } from '@testing-library/react-hooks/native';
|
|
4
|
+
import { waitFor } from '@testing-library/react-native';
|
|
5
|
+
|
|
6
|
+
import type { DefaultStreamChatGenerics } from 'src/types/types';
|
|
7
|
+
|
|
8
|
+
import type { DefaultGenerics, StreamChat } from 'stream-chat';
|
|
9
|
+
|
|
10
|
+
import { ChatContext, ChatContextValue } from '../../../../contexts/chatContext/ChatContext';
|
|
11
|
+
import {
|
|
12
|
+
CHANNEL,
|
|
13
|
+
CHANNEL_WITH_DELETED_MESSAGES,
|
|
14
|
+
CHANNEL_WITH_EMPTY_MESSAGE,
|
|
15
|
+
CHANNEL_WITH_MENTIONED_USERS,
|
|
16
|
+
CHANNEL_WITH_MESSAGES_ATTACHMENTS,
|
|
17
|
+
CHANNEL_WITH_MESSAGES_COMMAND,
|
|
18
|
+
CHANNEL_WITH_MESSAGES_TEXT,
|
|
19
|
+
CHANNEL_WITH_NO_MESSAGES,
|
|
20
|
+
LATEST_MESSAGE,
|
|
21
|
+
} from '../../../../mock-builders/api/channelMocks';
|
|
22
|
+
|
|
23
|
+
import { generateUser } from '../../../../mock-builders/generator/user';
|
|
24
|
+
import { getTestClientWithUser } from '../../../../mock-builders/mock';
|
|
25
|
+
|
|
26
|
+
import { useLatestMessagePreview } from '../useLatestMessagePreview';
|
|
27
|
+
|
|
28
|
+
describe('useLatestMessagePreview', () => {
|
|
29
|
+
const FORCE_UPDATE = 15;
|
|
30
|
+
const clientUser = generateUser();
|
|
31
|
+
let chatClient: StreamChat<DefaultGenerics> | StreamChat<DefaultStreamChatGenerics>;
|
|
32
|
+
|
|
33
|
+
beforeEach(async () => {
|
|
34
|
+
chatClient = await getTestClientWithUser(clientUser);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const ChatProvider: FC<{ children: React.ReactNode }> = ({ children }) => (
|
|
38
|
+
<ChatContext.Provider
|
|
39
|
+
value={
|
|
40
|
+
{
|
|
41
|
+
auto_translation_enabled: true,
|
|
42
|
+
client: chatClient,
|
|
43
|
+
} as unknown as ChatContextValue
|
|
44
|
+
}
|
|
45
|
+
>
|
|
46
|
+
{children}
|
|
47
|
+
</ChatContext.Provider>
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
it('should return a channel latest message', async () => {
|
|
51
|
+
const { result } = renderHook(
|
|
52
|
+
() => useLatestMessagePreview(CHANNEL, FORCE_UPDATE, LATEST_MESSAGE),
|
|
53
|
+
{ wrapper: ChatProvider },
|
|
54
|
+
);
|
|
55
|
+
await waitFor(() => {
|
|
56
|
+
expect(result.current.previews).toBeTruthy();
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('should return a deleted message preview', async () => {
|
|
61
|
+
const { result } = renderHook(
|
|
62
|
+
() => useLatestMessagePreview(CHANNEL_WITH_DELETED_MESSAGES, FORCE_UPDATE, LATEST_MESSAGE),
|
|
63
|
+
{ wrapper: ChatProvider },
|
|
64
|
+
);
|
|
65
|
+
await waitFor(() => {
|
|
66
|
+
expect(result.current.previews).toEqual([{ bold: false, text: 'Message deleted' }]);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('should return an "Nothing yet..." message preview', async () => {
|
|
71
|
+
const { result } = renderHook(
|
|
72
|
+
() => useLatestMessagePreview(CHANNEL_WITH_NO_MESSAGES, FORCE_UPDATE, LATEST_MESSAGE),
|
|
73
|
+
{ wrapper: ChatProvider },
|
|
74
|
+
);
|
|
75
|
+
await waitFor(() => {
|
|
76
|
+
expect(result.current.previews).toEqual([{ bold: false, text: 'Nothing yet...' }]);
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('should return latest message preview', async () => {
|
|
81
|
+
const { result } = renderHook(
|
|
82
|
+
() => useLatestMessagePreview(CHANNEL_WITH_MESSAGES_TEXT, FORCE_UPDATE, LATEST_MESSAGE),
|
|
83
|
+
{ wrapper: ChatProvider },
|
|
84
|
+
);
|
|
85
|
+
await waitFor(() => {
|
|
86
|
+
expect(result.current.previews).toEqual([
|
|
87
|
+
{ bold: true, text: '@okechukwu: ' },
|
|
88
|
+
{ bold: false, text: 'jkbkbiubicbi' },
|
|
89
|
+
]);
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('should return a channel with an empty message preview', async () => {
|
|
94
|
+
const { result } = renderHook(
|
|
95
|
+
() => useLatestMessagePreview(CHANNEL_WITH_EMPTY_MESSAGE, FORCE_UPDATE, LATEST_MESSAGE),
|
|
96
|
+
{ wrapper: ChatProvider },
|
|
97
|
+
);
|
|
98
|
+
await waitFor(() => {
|
|
99
|
+
expect(result.current.previews).toEqual([
|
|
100
|
+
{ bold: false, text: '' },
|
|
101
|
+
{ bold: false, text: 'Empty message...' },
|
|
102
|
+
]);
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('should return a mentioned user (@Max) message preview', async () => {
|
|
107
|
+
const { result } = renderHook(
|
|
108
|
+
() => useLatestMessagePreview(CHANNEL_WITH_MENTIONED_USERS, FORCE_UPDATE, LATEST_MESSAGE),
|
|
109
|
+
{ wrapper: ChatProvider },
|
|
110
|
+
);
|
|
111
|
+
await waitFor(() => {
|
|
112
|
+
expect(result.current.previews).toEqual([
|
|
113
|
+
{ bold: false, text: '' },
|
|
114
|
+
{ bold: false, text: 'Max' },
|
|
115
|
+
]);
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it('should return the latest command preview', async () => {
|
|
120
|
+
const { result } = renderHook(
|
|
121
|
+
() => useLatestMessagePreview(CHANNEL_WITH_MESSAGES_COMMAND, FORCE_UPDATE, LATEST_MESSAGE),
|
|
122
|
+
{ wrapper: ChatProvider },
|
|
123
|
+
);
|
|
124
|
+
await waitFor(() => {
|
|
125
|
+
expect(result.current.previews).toEqual([
|
|
126
|
+
{ bold: true, text: '@okechukwu: ' },
|
|
127
|
+
{ bold: false, text: '/giphy' },
|
|
128
|
+
]);
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('should return an attachment preview', async () => {
|
|
133
|
+
const { result } = renderHook(
|
|
134
|
+
() =>
|
|
135
|
+
useLatestMessagePreview(CHANNEL_WITH_MESSAGES_ATTACHMENTS, FORCE_UPDATE, LATEST_MESSAGE),
|
|
136
|
+
{ wrapper: ChatProvider },
|
|
137
|
+
);
|
|
138
|
+
await waitFor(() => {
|
|
139
|
+
expect(result.current.previews).toEqual([
|
|
140
|
+
{ bold: true, text: '@okechukwu: ' },
|
|
141
|
+
{ bold: false, text: '🏙 Attachment...' },
|
|
142
|
+
]);
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useEffect, useState } from 'react';
|
|
2
2
|
|
|
3
|
-
import type { Channel, ChannelState, MessageResponse, StreamChat } from 'stream-chat';
|
|
3
|
+
import type { Channel, ChannelState, MessageResponse, StreamChat, UserResponse } from 'stream-chat';
|
|
4
4
|
|
|
5
5
|
import { useChatContext } from '../../../contexts/chatContext/ChatContext';
|
|
6
6
|
import {
|
|
@@ -29,6 +29,43 @@ export type LatestMessagePreview<
|
|
|
29
29
|
status: number;
|
|
30
30
|
};
|
|
31
31
|
|
|
32
|
+
const getMessageSenderName = <
|
|
33
|
+
StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics,
|
|
34
|
+
>(
|
|
35
|
+
message: LatestMessage<StreamChatGenerics> | undefined,
|
|
36
|
+
currentUserId: string | undefined,
|
|
37
|
+
t: (key: string) => string,
|
|
38
|
+
membersLength: number,
|
|
39
|
+
) => {
|
|
40
|
+
if (message?.user?.id === currentUserId) {
|
|
41
|
+
return t('You');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (membersLength > 2) {
|
|
45
|
+
return message?.user?.name || message?.user?.username || message?.user?.id || '';
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return '';
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const getMentionUsers = <
|
|
52
|
+
StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics,
|
|
53
|
+
>(
|
|
54
|
+
mentionedUser: UserResponse<StreamChatGenerics>[] | undefined,
|
|
55
|
+
) => {
|
|
56
|
+
if (Array.isArray(mentionedUser)) {
|
|
57
|
+
const mentionUserString = mentionedUser.reduce((acc, cur) => {
|
|
58
|
+
const userName = cur.name || cur.id || '';
|
|
59
|
+
if (userName) {
|
|
60
|
+
acc += `${acc.length ? '|' : ''}@${userName}`;
|
|
61
|
+
}
|
|
62
|
+
return acc;
|
|
63
|
+
}, '');
|
|
64
|
+
return mentionUserString;
|
|
65
|
+
}
|
|
66
|
+
return '';
|
|
67
|
+
};
|
|
68
|
+
|
|
32
69
|
const getLatestMessageDisplayText = <
|
|
33
70
|
StreamChatGenerics extends DefaultStreamChatGenerics = DefaultStreamChatGenerics,
|
|
34
71
|
>(
|
|
@@ -40,32 +77,21 @@ const getLatestMessageDisplayText = <
|
|
|
40
77
|
if (!message) return [{ bold: false, text: t('Nothing yet...') }];
|
|
41
78
|
const isMessageTypeDeleted = message.type === 'deleted';
|
|
42
79
|
if (isMessageTypeDeleted) return [{ bold: false, text: t('Message deleted') }];
|
|
43
|
-
const currentUserId = client
|
|
44
|
-
const messageOwnerId = message.user?.id;
|
|
80
|
+
const currentUserId = client?.userID;
|
|
45
81
|
const members = Object.keys(channel.state.members);
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
const ownerText = owner ? `${owner === t('You') ? '' : '@'}${owner}: ` : '';
|
|
53
|
-
const boldOwner = ownerText.includes('@');
|
|
82
|
+
|
|
83
|
+
const messageSender = getMessageSenderName(message, currentUserId, t, members.length);
|
|
84
|
+
const messageSenderText = messageSender
|
|
85
|
+
? `${messageSender === t('You') ? '' : '@'}${messageSender}: `
|
|
86
|
+
: '';
|
|
87
|
+
const boldOwner = messageSenderText.includes('@');
|
|
54
88
|
if (message.text) {
|
|
55
89
|
// rough guess optimization to limit string preview to max 100 characters
|
|
56
90
|
const shortenedText = message.text.substring(0, 100).replace(/\n/g, ' ');
|
|
57
|
-
const mentionedUsers =
|
|
58
|
-
? message.mentioned_users.reduce((acc, cur) => {
|
|
59
|
-
const userName = cur.name || cur.id || '';
|
|
60
|
-
if (userName) {
|
|
61
|
-
acc += `${acc.length ? '|' : ''}@${userName}`;
|
|
62
|
-
}
|
|
63
|
-
return acc;
|
|
64
|
-
}, '')
|
|
65
|
-
: '';
|
|
91
|
+
const mentionedUsers = getMentionUsers(message.mentioned_users);
|
|
66
92
|
const regEx = new RegExp(`^(${mentionedUsers})`);
|
|
67
93
|
return [
|
|
68
|
-
{ bold: boldOwner, text:
|
|
94
|
+
{ bold: boldOwner, text: messageSenderText },
|
|
69
95
|
...shortenedText.split('').reduce(
|
|
70
96
|
(acc, cur, index) => {
|
|
71
97
|
if (cur === '@' && mentionedUsers && regEx.test(shortenedText.substring(index))) {
|
|
@@ -83,18 +109,18 @@ const getLatestMessageDisplayText = <
|
|
|
83
109
|
}
|
|
84
110
|
if (message.command) {
|
|
85
111
|
return [
|
|
86
|
-
{ bold: boldOwner, text:
|
|
112
|
+
{ bold: boldOwner, text: messageSenderText },
|
|
87
113
|
{ bold: false, text: `/${message.command}` },
|
|
88
114
|
];
|
|
89
115
|
}
|
|
90
116
|
if (message.attachments?.length) {
|
|
91
117
|
return [
|
|
92
|
-
{ bold: boldOwner, text:
|
|
118
|
+
{ bold: boldOwner, text: messageSenderText },
|
|
93
119
|
{ bold: false, text: t('🏙 Attachment...') },
|
|
94
120
|
];
|
|
95
121
|
}
|
|
96
122
|
return [
|
|
97
|
-
{ bold: boldOwner, text:
|
|
123
|
+
{ bold: boldOwner, text: messageSenderText },
|
|
98
124
|
{ bold: false, text: t('Empty message...') },
|
|
99
125
|
];
|
|
100
126
|
};
|
|
@@ -45,11 +45,11 @@ const styles = StyleSheet.create({
|
|
|
45
45
|
filenameText: {
|
|
46
46
|
fontSize: 14,
|
|
47
47
|
fontWeight: 'bold',
|
|
48
|
-
|
|
48
|
+
paddingHorizontal: 10,
|
|
49
49
|
},
|
|
50
50
|
fileSizeText: {
|
|
51
51
|
fontSize: 12,
|
|
52
|
-
|
|
52
|
+
paddingHorizontal: 10,
|
|
53
53
|
},
|
|
54
54
|
fileTextContainer: {
|
|
55
55
|
height: '100%',
|
|
@@ -66,11 +66,12 @@ const styles = StyleSheet.create({
|
|
|
66
66
|
paddingLeft: 10,
|
|
67
67
|
},
|
|
68
68
|
unsupportedFileText: {
|
|
69
|
-
fontSize:
|
|
69
|
+
fontSize: 12,
|
|
70
|
+
marginHorizontal: 4,
|
|
70
71
|
},
|
|
71
72
|
warningIconStyle: {
|
|
72
73
|
borderRadius: 24,
|
|
73
|
-
marginTop:
|
|
74
|
+
marginTop: 3,
|
|
74
75
|
},
|
|
75
76
|
});
|
|
76
77
|
|