stream-chat-react-native-core 5.8.0-beta.1 → 5.8.0-beta.3
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/Channel/Channel.js +35 -32
- package/lib/commonjs/components/Channel/Channel.js.map +1 -1
- package/lib/commonjs/components/MessageInput/MessageInput.js +50 -34
- package/lib/commonjs/components/MessageInput/MessageInput.js.map +1 -1
- package/lib/commonjs/contexts/messageInputContext/MessageInputContext.js +17 -7
- package/lib/commonjs/contexts/messageInputContext/MessageInputContext.js.map +1 -1
- package/lib/commonjs/index.js +14 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/utils/patchMessageTextCommand.js +26 -0
- package/lib/commonjs/utils/patchMessageTextCommand.js.map +1 -0
- package/lib/commonjs/version.json +1 -1
- package/lib/module/components/Channel/Channel.js +35 -32
- package/lib/module/components/Channel/Channel.js.map +1 -1
- package/lib/module/components/MessageInput/MessageInput.js +50 -34
- package/lib/module/components/MessageInput/MessageInput.js.map +1 -1
- package/lib/module/contexts/messageInputContext/MessageInputContext.js +17 -7
- package/lib/module/contexts/messageInputContext/MessageInputContext.js.map +1 -1
- package/lib/module/index.js +14 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/utils/patchMessageTextCommand.js +26 -0
- package/lib/module/utils/patchMessageTextCommand.js.map +1 -0
- package/lib/module/version.json +1 -1
- package/lib/typescript/index.d.ts +1 -0
- package/lib/typescript/utils/__tests__/patchMessageTextCommand.test.d.ts +1 -0
- package/lib/typescript/utils/patchMessageTextCommand.d.ts +9 -0
- package/package.json +2 -2
- package/src/components/Channel/Channel.tsx +5 -2
- package/src/components/MessageInput/MessageInput.tsx +34 -5
- package/src/components/MessageInput/__tests__/MessageInput.test.js +61 -11
- package/src/contexts/messageInputContext/MessageInputContext.tsx +21 -10
- package/src/contexts/messageInputContext/__tests__/pickFile.test.tsx +26 -0
- package/src/index.ts +1 -0
- package/src/utils/__tests__/patchMessageTextCommand.test.ts +14 -0
- package/src/utils/patchMessageTextCommand.ts +35 -0
- package/src/version.json +1 -1
- package/src/components/MessageInput/__tests__/__snapshots__/MessageInput.test.js.snap +0 -746
|
@@ -1,19 +1,63 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
|
|
3
|
+
import { Alert } from 'react-native';
|
|
4
|
+
|
|
3
5
|
import { cleanup, fireEvent, render, waitFor } from '@testing-library/react-native';
|
|
4
6
|
|
|
7
|
+
import * as AttachmentPickerUtils from '../../../contexts/attachmentPickerContext/AttachmentPickerContext';
|
|
5
8
|
import { OverlayProvider } from '../../../contexts/overlayContext/OverlayProvider';
|
|
6
9
|
import { getOrCreateChannelApi } from '../../../mock-builders/api/getOrCreateChannel';
|
|
7
10
|
|
|
8
11
|
import { useMockedApis } from '../../../mock-builders/api/useMockedApis';
|
|
12
|
+
import {
|
|
13
|
+
generateFileAttachment,
|
|
14
|
+
generateImageAttachment,
|
|
15
|
+
} from '../../../mock-builders/generator/attachment';
|
|
9
16
|
import { generateChannelResponse } from '../../../mock-builders/generator/channel';
|
|
10
17
|
import { generateUser } from '../../../mock-builders/generator/user';
|
|
11
18
|
import { getTestClientWithUser } from '../../../mock-builders/mock';
|
|
19
|
+
import { CameraSelectorIcon } from '../../AttachmentPicker/components/CameraSelectorIcon';
|
|
20
|
+
import { FileSelectorIcon } from '../../AttachmentPicker/components/FileSelectorIcon';
|
|
21
|
+
import { ImageSelectorIcon } from '../../AttachmentPicker/components/ImageSelectorIcon';
|
|
12
22
|
import { Channel } from '../../Channel/Channel';
|
|
13
23
|
import { Chat } from '../../Chat/Chat';
|
|
14
24
|
import { MessageInput } from '../MessageInput';
|
|
15
25
|
|
|
16
26
|
describe('MessageInput', () => {
|
|
27
|
+
jest.spyOn(Alert, 'alert');
|
|
28
|
+
jest.spyOn(AttachmentPickerUtils, 'useAttachmentPickerContext').mockImplementation(
|
|
29
|
+
jest.fn(() => ({
|
|
30
|
+
CameraSelectorIcon,
|
|
31
|
+
closePicker: jest.fn(),
|
|
32
|
+
FileSelectorIcon,
|
|
33
|
+
ImageSelectorIcon,
|
|
34
|
+
openPicker: jest.fn(),
|
|
35
|
+
selectedFiles: [
|
|
36
|
+
generateFileAttachment({ name: 'Dummy.pdf', size: 500000000 }),
|
|
37
|
+
generateFileAttachment({ name: 'Dummy.pdf', size: 600000000 }),
|
|
38
|
+
],
|
|
39
|
+
selectedImages: [
|
|
40
|
+
generateImageAttachment({
|
|
41
|
+
file: { height: 100, uri: 'https://picsum.photos/200/300', width: 100 },
|
|
42
|
+
fileSize: 500000000,
|
|
43
|
+
uri: 'https://picsum.photos/200/300',
|
|
44
|
+
}),
|
|
45
|
+
generateImageAttachment({
|
|
46
|
+
file: { height: 100, uri: 'https://picsum.photos/200/300', width: 100 },
|
|
47
|
+
fileSize: 600000000,
|
|
48
|
+
uri: 'https://picsum.photos/200/300',
|
|
49
|
+
}),
|
|
50
|
+
],
|
|
51
|
+
selectedPicker: 'images',
|
|
52
|
+
setBottomInset: jest.fn(),
|
|
53
|
+
setMaxNumberOfFiles: jest.fn(),
|
|
54
|
+
setSelectedFiles: jest.fn(),
|
|
55
|
+
setSelectedImages: jest.fn(),
|
|
56
|
+
setSelectedPicker: jest.fn(),
|
|
57
|
+
setTopInset: jest.fn(),
|
|
58
|
+
})),
|
|
59
|
+
);
|
|
60
|
+
|
|
17
61
|
const clientUser = generateUser();
|
|
18
62
|
let chatClient;
|
|
19
63
|
let channel;
|
|
@@ -47,17 +91,12 @@ describe('MessageInput', () => {
|
|
|
47
91
|
|
|
48
92
|
it('should render MessageInput', async () => {
|
|
49
93
|
await initializeChannel(generateChannelResponse());
|
|
94
|
+
|
|
50
95
|
const openPicker = jest.fn();
|
|
51
96
|
const closePicker = jest.fn();
|
|
52
97
|
const attachmentValue = { closePicker, openPicker };
|
|
53
98
|
|
|
54
|
-
const { getByTestId, queryByTestId, queryByText
|
|
55
|
-
getComponent({ attachmentValue }),
|
|
56
|
-
);
|
|
57
|
-
|
|
58
|
-
await waitFor(() => {
|
|
59
|
-
expect(queryByTestId('attach-button')).toBeTruthy();
|
|
60
|
-
});
|
|
99
|
+
const { getByTestId, queryByTestId, queryByText } = render(getComponent({ attachmentValue }));
|
|
61
100
|
|
|
62
101
|
fireEvent.press(getByTestId('attach-button'));
|
|
63
102
|
|
|
@@ -69,11 +108,22 @@ describe('MessageInput', () => {
|
|
|
69
108
|
expect(queryByTestId('send-button')).toBeTruthy();
|
|
70
109
|
expect(queryByText('Editing Message')).toBeFalsy();
|
|
71
110
|
});
|
|
111
|
+
});
|
|
72
112
|
|
|
73
|
-
|
|
113
|
+
it('trigger file size threshold limit alert when images size above the limit', async () => {
|
|
114
|
+
await initializeChannel(generateChannelResponse());
|
|
74
115
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
116
|
+
render(
|
|
117
|
+
<OverlayProvider>
|
|
118
|
+
<Chat client={chatClient}>
|
|
119
|
+
<Channel channel={channel}>
|
|
120
|
+
<MessageInput />
|
|
121
|
+
</Channel>
|
|
122
|
+
</Chat>
|
|
123
|
+
</OverlayProvider>,
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
// Both for files and for images triggered in one test itself.
|
|
127
|
+
expect(Alert.alert).toHaveBeenCalledTimes(4);
|
|
78
128
|
});
|
|
79
129
|
});
|
|
@@ -590,17 +590,28 @@ export const MessageInputProvider = <
|
|
|
590
590
|
const result = await pickDocument({
|
|
591
591
|
maxNumberOfFiles: value.maxNumberOfFiles - numberOfUploads,
|
|
592
592
|
});
|
|
593
|
+
|
|
594
|
+
const MEGA_BYTES_TO_BYTES = 1024 * 1024;
|
|
595
|
+
const MAX_FILE_SIZE_TO_UPLOAD_IN_MB = 100;
|
|
596
|
+
|
|
593
597
|
if (!result.cancelled && result.docs) {
|
|
594
|
-
result.docs.
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
598
|
+
const totalFileSize = result.docs.reduce((acc, doc) => acc + Number(doc.size), 0);
|
|
599
|
+
if (totalFileSize / MEGA_BYTES_TO_BYTES > MAX_FILE_SIZE_TO_UPLOAD_IN_MB) {
|
|
600
|
+
Alert.alert(
|
|
601
|
+
`Maximum file size upload limit reached, please upload files below ${MAX_FILE_SIZE_TO_UPLOAD_IN_MB}MB.`,
|
|
602
|
+
);
|
|
603
|
+
} else {
|
|
604
|
+
result.docs.forEach((doc) => {
|
|
605
|
+
/**
|
|
606
|
+
* TODO: The current tight coupling of images to the image
|
|
607
|
+
* picker does not allow images picked from the file picker
|
|
608
|
+
* to be rendered in a preview via the uploadNewImage call.
|
|
609
|
+
* This should be updated alongside allowing image a file
|
|
610
|
+
* uploads together.
|
|
611
|
+
*/
|
|
612
|
+
uploadNewFile(doc);
|
|
613
|
+
});
|
|
614
|
+
}
|
|
604
615
|
}
|
|
605
616
|
};
|
|
606
617
|
|
|
@@ -4,6 +4,10 @@ import { act } from 'react-test-renderer';
|
|
|
4
4
|
|
|
5
5
|
import { renderHook } from '@testing-library/react-hooks';
|
|
6
6
|
|
|
7
|
+
import { generateFileAttachment } from '../../../mock-builders/generator/attachment';
|
|
8
|
+
|
|
9
|
+
import * as NativeUtils from '../../../native';
|
|
10
|
+
|
|
7
11
|
import type { DefaultStreamChatGenerics } from '../../../types/types';
|
|
8
12
|
import {
|
|
9
13
|
InputMessageInputContextValue,
|
|
@@ -34,6 +38,15 @@ afterEach(jest.clearAllMocks);
|
|
|
34
38
|
|
|
35
39
|
describe("MessageInputContext's pickFile", () => {
|
|
36
40
|
jest.spyOn(Alert, 'alert');
|
|
41
|
+
jest.spyOn(NativeUtils, 'pickDocument').mockImplementation(
|
|
42
|
+
jest.fn().mockResolvedValue({
|
|
43
|
+
cancelled: false,
|
|
44
|
+
docs: [
|
|
45
|
+
generateFileAttachment({ size: 500000000 }),
|
|
46
|
+
generateFileAttachment({ size: 600000000 }),
|
|
47
|
+
],
|
|
48
|
+
}),
|
|
49
|
+
);
|
|
37
50
|
|
|
38
51
|
const initialProps = {
|
|
39
52
|
editing: true,
|
|
@@ -64,4 +77,17 @@ describe("MessageInputContext's pickFile", () => {
|
|
|
64
77
|
expect(Alert.alert).toHaveBeenCalledTimes(numberOfTimesCalled);
|
|
65
78
|
},
|
|
66
79
|
);
|
|
80
|
+
|
|
81
|
+
it('trigger file size threshold limit alert when file size above the limit', () => {
|
|
82
|
+
const { result } = renderHook(() => useMessageInputContext(), {
|
|
83
|
+
initialProps,
|
|
84
|
+
wrapper: Wrapper,
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
act(() => {
|
|
88
|
+
result.current.pickFile();
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
expect(Alert.alert).toHaveBeenCalledTimes(1);
|
|
92
|
+
});
|
|
67
93
|
});
|
package/src/index.ts
CHANGED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { patchMessageTextCommand } from '../patchMessageTextCommand';
|
|
2
|
+
|
|
3
|
+
describe('patchMessageTextCommand', () => {
|
|
4
|
+
test.each<[string, string, string[]]>([
|
|
5
|
+
['/ban @santhosh vaiyapuri reason', '/ban @santhosh reason', ['santhosh']],
|
|
6
|
+
['/ban @santhosh vaiyapuri', '/ban @santhosh vaiyapuri', ['santhosh']],
|
|
7
|
+
['/ban @santhosh', '/ban @santhosh @santhosh', ['santhosh']],
|
|
8
|
+
['/ban @santhosh vaiyapuri @khushal reason', '/ban @santhosh reason', ['santhosh', 'khushal']],
|
|
9
|
+
['/mute @santhosh vaiyapuri', '/mute @santhosh', ['santhosh']],
|
|
10
|
+
['/mute @santhosh vaiyapuri @khushal reason', '/mute @santhosh', ['santhosh', 'khushal']],
|
|
11
|
+
])('Patch message with text:"%s" to "%s")', (input, expectedOutput, mentionedUserIds) => {
|
|
12
|
+
expect(patchMessageTextCommand(input, mentionedUserIds)).toBe(expectedOutput);
|
|
13
|
+
});
|
|
14
|
+
});
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Converts a message text command to proper format
|
|
3
|
+
* Example: "/mute @username" to "/mute @userId"
|
|
4
|
+
* Supports "/ban", "/unban", "/mute", "/unmute"
|
|
5
|
+
* @param messageText
|
|
6
|
+
* @param mentionedUsers
|
|
7
|
+
* @returns
|
|
8
|
+
*/
|
|
9
|
+
export function patchMessageTextCommand(messageText: string, mentionedUserIds: string[]): string {
|
|
10
|
+
if (mentionedUserIds.length === 0) {
|
|
11
|
+
return messageText;
|
|
12
|
+
}
|
|
13
|
+
const trimmedMessageText = messageText.trim();
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The required formats are "/unban @userid" or "/mute @userid" or "/unmute @userid"
|
|
17
|
+
*/
|
|
18
|
+
if (
|
|
19
|
+
trimmedMessageText.startsWith('/mute ') ||
|
|
20
|
+
trimmedMessageText.startsWith('/unmute ') ||
|
|
21
|
+
trimmedMessageText.startsWith('/unban ')
|
|
22
|
+
) {
|
|
23
|
+
return trimmedMessageText.replace(/@.+/, `@${mentionedUserIds[0]}`);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* The required format is "/ban @userid reason"
|
|
28
|
+
*/
|
|
29
|
+
if (trimmedMessageText.startsWith('/ban ')) {
|
|
30
|
+
const reasonText = trimmedMessageText.split(' ').pop() ?? '';
|
|
31
|
+
return `/ban @${mentionedUserIds[0]} ${reasonText}`.trim();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return messageText;
|
|
35
|
+
}
|
package/src/version.json
CHANGED