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.
Files changed (36) hide show
  1. package/lib/commonjs/components/Channel/Channel.js +35 -32
  2. package/lib/commonjs/components/Channel/Channel.js.map +1 -1
  3. package/lib/commonjs/components/MessageInput/MessageInput.js +50 -34
  4. package/lib/commonjs/components/MessageInput/MessageInput.js.map +1 -1
  5. package/lib/commonjs/contexts/messageInputContext/MessageInputContext.js +17 -7
  6. package/lib/commonjs/contexts/messageInputContext/MessageInputContext.js.map +1 -1
  7. package/lib/commonjs/index.js +14 -0
  8. package/lib/commonjs/index.js.map +1 -1
  9. package/lib/commonjs/utils/patchMessageTextCommand.js +26 -0
  10. package/lib/commonjs/utils/patchMessageTextCommand.js.map +1 -0
  11. package/lib/commonjs/version.json +1 -1
  12. package/lib/module/components/Channel/Channel.js +35 -32
  13. package/lib/module/components/Channel/Channel.js.map +1 -1
  14. package/lib/module/components/MessageInput/MessageInput.js +50 -34
  15. package/lib/module/components/MessageInput/MessageInput.js.map +1 -1
  16. package/lib/module/contexts/messageInputContext/MessageInputContext.js +17 -7
  17. package/lib/module/contexts/messageInputContext/MessageInputContext.js.map +1 -1
  18. package/lib/module/index.js +14 -0
  19. package/lib/module/index.js.map +1 -1
  20. package/lib/module/utils/patchMessageTextCommand.js +26 -0
  21. package/lib/module/utils/patchMessageTextCommand.js.map +1 -0
  22. package/lib/module/version.json +1 -1
  23. package/lib/typescript/index.d.ts +1 -0
  24. package/lib/typescript/utils/__tests__/patchMessageTextCommand.test.d.ts +1 -0
  25. package/lib/typescript/utils/patchMessageTextCommand.d.ts +9 -0
  26. package/package.json +2 -2
  27. package/src/components/Channel/Channel.tsx +5 -2
  28. package/src/components/MessageInput/MessageInput.tsx +34 -5
  29. package/src/components/MessageInput/__tests__/MessageInput.test.js +61 -11
  30. package/src/contexts/messageInputContext/MessageInputContext.tsx +21 -10
  31. package/src/contexts/messageInputContext/__tests__/pickFile.test.tsx +26 -0
  32. package/src/index.ts +1 -0
  33. package/src/utils/__tests__/patchMessageTextCommand.test.ts +14 -0
  34. package/src/utils/patchMessageTextCommand.ts +35 -0
  35. package/src/version.json +1 -1
  36. 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, toJSON } = render(
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
- const snapshot = toJSON();
113
+ it('trigger file size threshold limit alert when images size above the limit', async () => {
114
+ await initializeChannel(generateChannelResponse());
74
115
 
75
- await waitFor(() => {
76
- expect(snapshot).toMatchSnapshot();
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.forEach((doc) => {
595
- /**
596
- * TODO: The current tight coupling of images to the image
597
- * picker does not allow images picked from the file picker
598
- * to be rendered in a preview via the uploadNewImage call.
599
- * This should be updated alongside allowing image a file
600
- * uploads together.
601
- */
602
- uploadNewFile(doc);
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
@@ -8,6 +8,7 @@ export * from './icons';
8
8
 
9
9
  export * from './types/types';
10
10
 
11
+ export * from './utils/patchMessageTextCommand';
11
12
  export * from './utils/Streami18n';
12
13
  export * from './utils/utils';
13
14
 
@@ -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
@@ -1,3 +1,3 @@
1
1
  {
2
- "version": "5.8.0-beta.1"
2
+ "version": "5.8.0-beta.3"
3
3
  }