stream-chat-react-native-core 9.6.0 → 9.6.1-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.
Files changed (63) hide show
  1. package/lib/commonjs/components/Channel/Channel.js +3 -0
  2. package/lib/commonjs/components/Channel/Channel.js.map +1 -1
  3. package/lib/commonjs/components/Channel/hooks/useCreateInputMessageInputContext.js +2 -0
  4. package/lib/commonjs/components/Channel/hooks/useCreateInputMessageInputContext.js.map +1 -1
  5. package/lib/commonjs/components/MessageInput/components/InputButtons/AttachButton.js +8 -2
  6. package/lib/commonjs/components/MessageInput/components/InputButtons/AttachButton.js.map +1 -1
  7. package/lib/commonjs/components/MessageMenu/EmojiPickerList.js +3 -2
  8. package/lib/commonjs/components/MessageMenu/EmojiPickerList.js.map +1 -1
  9. package/lib/commonjs/components/UIComponents/BottomSheetModal.js +6 -1
  10. package/lib/commonjs/components/UIComponents/BottomSheetModal.js.map +1 -1
  11. package/lib/commonjs/contexts/messageInputContext/MessageInputContext.js.map +1 -1
  12. package/lib/commonjs/contexts/messageInputContext/hooks/useCreateMessageInputContext.js +2 -0
  13. package/lib/commonjs/contexts/messageInputContext/hooks/useCreateMessageInputContext.js.map +1 -1
  14. package/lib/commonjs/hooks/index.js +11 -0
  15. package/lib/commonjs/hooks/index.js.map +1 -1
  16. package/lib/commonjs/hooks/useScreenOrientation.js +13 -0
  17. package/lib/commonjs/hooks/useScreenOrientation.js.map +1 -0
  18. package/lib/commonjs/version.json +1 -1
  19. package/lib/module/components/Channel/Channel.js +3 -0
  20. package/lib/module/components/Channel/Channel.js.map +1 -1
  21. package/lib/module/components/Channel/hooks/useCreateInputMessageInputContext.js +2 -0
  22. package/lib/module/components/Channel/hooks/useCreateInputMessageInputContext.js.map +1 -1
  23. package/lib/module/components/MessageInput/components/InputButtons/AttachButton.js +8 -2
  24. package/lib/module/components/MessageInput/components/InputButtons/AttachButton.js.map +1 -1
  25. package/lib/module/components/MessageMenu/EmojiPickerList.js +3 -2
  26. package/lib/module/components/MessageMenu/EmojiPickerList.js.map +1 -1
  27. package/lib/module/components/UIComponents/BottomSheetModal.js +6 -1
  28. package/lib/module/components/UIComponents/BottomSheetModal.js.map +1 -1
  29. package/lib/module/contexts/messageInputContext/MessageInputContext.js.map +1 -1
  30. package/lib/module/contexts/messageInputContext/hooks/useCreateMessageInputContext.js +2 -0
  31. package/lib/module/contexts/messageInputContext/hooks/useCreateMessageInputContext.js.map +1 -1
  32. package/lib/module/hooks/index.js +11 -0
  33. package/lib/module/hooks/index.js.map +1 -1
  34. package/lib/module/hooks/useScreenOrientation.js +13 -0
  35. package/lib/module/hooks/useScreenOrientation.js.map +1 -0
  36. package/lib/module/version.json +1 -1
  37. package/lib/typescript/components/Channel/Channel.d.ts +1 -1
  38. package/lib/typescript/components/Channel/Channel.d.ts.map +1 -1
  39. package/lib/typescript/components/Channel/hooks/useCreateInputMessageInputContext.d.ts +1 -1
  40. package/lib/typescript/components/Channel/hooks/useCreateInputMessageInputContext.d.ts.map +1 -1
  41. package/lib/typescript/components/MessageInput/components/InputButtons/AttachButton.d.ts.map +1 -1
  42. package/lib/typescript/components/MessageMenu/EmojiPickerList.d.ts.map +1 -1
  43. package/lib/typescript/components/UIComponents/BottomSheetModal.d.ts.map +1 -1
  44. package/lib/typescript/contexts/messageInputContext/MessageInputContext.d.ts +8 -0
  45. package/lib/typescript/contexts/messageInputContext/MessageInputContext.d.ts.map +1 -1
  46. package/lib/typescript/contexts/messageInputContext/hooks/useCreateMessageInputContext.d.ts +1 -1
  47. package/lib/typescript/contexts/messageInputContext/hooks/useCreateMessageInputContext.d.ts.map +1 -1
  48. package/lib/typescript/hooks/index.d.ts +1 -0
  49. package/lib/typescript/hooks/index.d.ts.map +1 -1
  50. package/lib/typescript/hooks/useScreenOrientation.d.ts +10 -0
  51. package/lib/typescript/hooks/useScreenOrientation.d.ts.map +1 -0
  52. package/package.json +1 -1
  53. package/src/components/Channel/Channel.tsx +3 -0
  54. package/src/components/Channel/hooks/useCreateInputMessageInputContext.ts +2 -0
  55. package/src/components/MessageInput/__tests__/AttachButton.test.tsx +31 -0
  56. package/src/components/MessageInput/components/InputButtons/AttachButton.tsx +12 -2
  57. package/src/components/MessageMenu/EmojiPickerList.tsx +4 -2
  58. package/src/components/UIComponents/BottomSheetModal.tsx +19 -2
  59. package/src/contexts/messageInputContext/MessageInputContext.tsx +9 -0
  60. package/src/contexts/messageInputContext/hooks/useCreateMessageInputContext.ts +2 -0
  61. package/src/hooks/index.ts +1 -0
  62. package/src/hooks/useScreenOrientation.ts +15 -0
  63. package/src/version.json +1 -1
@@ -15,6 +15,7 @@ export * from './useSelectedChannelState';
15
15
  export * from './useStreami18n';
16
16
  export * from './useViewport';
17
17
  export * from './useScreenDimensions';
18
+ export * from './useScreenOrientation';
18
19
  export * from './useStateStore';
19
20
  export * from './usePendingAttachmentUpload';
20
21
  export * from './useStableCallback';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,WAAW,CAAC;AAC1B,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,qBAAqB,CAAC;AACpC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,wBAAwB,CAAC;AACvC,cAAc,mBAAmB,CAAC;AAClC,cAAc,sBAAsB,CAAC;AACrC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,uBAAuB,CAAC;AACtC,cAAc,iBAAiB,CAAC;AAChC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,YAAY,CAAC;AAC3B,cAAc,sBAAsB,CAAC;AACrC,cAAc,qBAAqB,CAAC;AACpC,cAAc,gCAAgC,CAAC;AAC/C,cAAc,0BAA0B,CAAC;AACzC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,mCAAmC,CAAC;AAClD,cAAc,4BAA4B,CAAC;AAC3C,cAAc,wBAAwB,CAAC;AACvC,cAAc,kBAAkB,CAAC;AACjC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,2CAA2C,CAAC;AAC1D,cAAc,wCAAwC,CAAC;AACvD,cAAc,wCAAwC,CAAC;AACvD,cAAc,wCAAwC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,WAAW,CAAC;AAC1B,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,qBAAqB,CAAC;AACpC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,wBAAwB,CAAC;AACvC,cAAc,mBAAmB,CAAC;AAClC,cAAc,sBAAsB,CAAC;AACrC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,uBAAuB,CAAC;AACtC,cAAc,wBAAwB,CAAC;AACvC,cAAc,iBAAiB,CAAC;AAChC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,YAAY,CAAC;AAC3B,cAAc,sBAAsB,CAAC;AACrC,cAAc,qBAAqB,CAAC;AACpC,cAAc,gCAAgC,CAAC;AAC/C,cAAc,0BAA0B,CAAC;AACzC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,mCAAmC,CAAC;AAClD,cAAc,4BAA4B,CAAC;AAC3C,cAAc,wBAAwB,CAAC;AACvC,cAAc,kBAAkB,CAAC;AACjC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,2CAA2C,CAAC;AAC1D,cAAc,wCAAwC,CAAC;AACvD,cAAc,wCAAwC,CAAC;AACvD,cAAc,wCAAwC,CAAC"}
@@ -0,0 +1,10 @@
1
+ export type ScreenOrientation = 'landscape' | 'portrait';
2
+ /**
3
+ * A custom hook that derives the current screen orientation from the window
4
+ * dimensions. It updates automatically on device rotation via
5
+ * `useWindowDimensions`.
6
+ *
7
+ * @returns {ScreenOrientation} Either `'landscape'` or `'portrait'`.
8
+ */
9
+ export declare const useScreenOrientation: () => ScreenOrientation;
10
+ //# sourceMappingURL=useScreenOrientation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useScreenOrientation.d.ts","sourceRoot":"","sources":["../../../src/hooks/useScreenOrientation.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,iBAAiB,GAAG,WAAW,GAAG,UAAU,CAAC;AAEzD;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,QAAO,iBAGvC,CAAC"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "stream-chat-react-native-core",
3
3
  "description": "The official React Native and Expo components for Stream Chat, a service for building chat applications",
4
- "version": "9.6.0",
4
+ "version": "9.6.1-beta.2",
5
5
  "author": {
6
6
  "company": "Stream.io Inc",
7
7
  "name": "Stream.io Inc"
@@ -218,6 +218,7 @@ export type ChannelPropsWithContext = Pick<ChannelContextValue, 'channel'> &
218
218
  | 'compressImageQuality'
219
219
  | 'createPollOptionGap'
220
220
  | 'doFileUploadRequest'
221
+ | 'focusInputOnPickerClose'
221
222
  | 'handleAttachButtonPress'
222
223
  | 'hasCameraPicker'
223
224
  | 'hasCommands'
@@ -423,6 +424,7 @@ const ChannelWithContext = (props: PropsWithChildren<ChannelPropsWithContext>) =
423
424
  enableSwipeToReply = true,
424
425
  enforceUniqueReaction = false,
425
426
  FlatList = NativeHandlers.FlatList,
427
+ focusInputOnPickerClose = true,
426
428
  forceAlignMessages,
427
429
  getMessageGroupStyle,
428
430
  handleAttachButtonPress,
@@ -1651,6 +1653,7 @@ const ChannelWithContext = (props: PropsWithChildren<ChannelPropsWithContext>) =
1651
1653
  createPollOptionGap,
1652
1654
  doFileUploadRequest,
1653
1655
  editMessage,
1656
+ focusInputOnPickerClose,
1654
1657
  handleAttachButtonPress,
1655
1658
  hasCameraPicker,
1656
1659
  hasCommands: hasCommands ?? !!clientChannelConfig?.commands?.length,
@@ -17,6 +17,7 @@ export const useCreateInputMessageInputContext = ({
17
17
  createPollOptionGap,
18
18
  doFileUploadRequest,
19
19
  editMessage,
20
+ focusInputOnPickerClose,
20
21
  handleAttachButtonPress,
21
22
  hasCameraPicker,
22
23
  hasCommands,
@@ -49,6 +50,7 @@ export const useCreateInputMessageInputContext = ({
49
50
  createPollOptionGap,
50
51
  doFileUploadRequest,
51
52
  editMessage,
53
+ focusInputOnPickerClose,
52
54
  handleAttachButtonPress,
53
55
  hasCameraPicker,
54
56
  hasCommands,
@@ -180,4 +180,35 @@ describe('AttachButton', () => {
180
180
  expect(queryByTestId('attachment-picker-list')).toBeTruthy();
181
181
  });
182
182
  });
183
+
184
+ it('should close the attachment picker on second press when focusInputOnPickerClose is false', async () => {
185
+ jest.spyOn(NativeHandler, 'isImageMediaLibraryAvailable').mockImplementation(() => true);
186
+
187
+ const channelProps = { channel, focusInputOnPickerClose: false };
188
+ const props = {};
189
+
190
+ renderComponent({ channelProps, client, props });
191
+
192
+ const { queryByTestId } = screen;
193
+
194
+ await waitFor(() => {
195
+ expect(queryByTestId('attach-button')).toBeTruthy();
196
+ });
197
+
198
+ act(() => {
199
+ fireEvent.press(screen.getByTestId('attach-button'));
200
+ });
201
+
202
+ await waitFor(() => {
203
+ expect(queryByTestId('attachment-picker-list')).toBeTruthy();
204
+ });
205
+
206
+ act(() => {
207
+ fireEvent.press(screen.getByTestId('attach-button'));
208
+ });
209
+
210
+ await waitFor(() => {
211
+ expect(queryByTestId('attachment-picker-list')).toBeNull();
212
+ });
213
+ });
183
214
  });
@@ -92,13 +92,23 @@ export type AttachButtonProps = Partial<AttachButtonPropsWithContext>;
92
92
  */
93
93
  export const AttachButton = (props: AttachButtonProps) => {
94
94
  const { disableAttachmentPicker } = useAttachmentPickerContext();
95
- const { inputBoxRef, handleAttachButtonPress, openAttachmentPicker } = useMessageInputContext();
95
+ const {
96
+ closeAttachmentPicker,
97
+ focusInputOnPickerClose,
98
+ handleAttachButtonPress,
99
+ inputBoxRef,
100
+ openAttachmentPicker,
101
+ } = useMessageInputContext();
96
102
  const { attachmentPickerStore } = useAttachmentPickerContext();
97
103
  const { selectedPicker } = useAttachmentPickerState();
98
104
 
99
105
  const toggleAttachmentPicker = useStableCallback(() => {
100
106
  if (attachmentPickerStore.state.getLatestValue().selectedPicker) {
101
- inputBoxRef.current?.focus();
107
+ if (focusInputOnPickerClose) {
108
+ inputBoxRef.current?.focus();
109
+ } else {
110
+ closeAttachmentPicker();
111
+ }
102
112
  } else {
103
113
  openAttachmentPicker();
104
114
  }
@@ -13,6 +13,7 @@ import { primitives } from '../../theme';
13
13
  import { StreamBottomSheetModalFlatList } from '../UIComponents';
14
14
 
15
15
  const EMOJI_SIZE = Platform.OS === 'ios' ? 32 : 28;
16
+ const EMOJI_CELL_SIZE = 54;
16
17
 
17
18
  const emojiKeyExtractor = (item: string) => `unicode-${item}`;
18
19
 
@@ -54,6 +55,7 @@ export const EmojiPickerList = ({
54
55
  renderFullInitially?: boolean;
55
56
  }) => {
56
57
  const styles = useStyles();
58
+
57
59
  const onSelectEmoji = useStableCallback((emoji: string) => {
58
60
  const scalarString = toUnicodeScalarString(emoji);
59
61
  onSelectReaction(scalarString);
@@ -94,9 +96,9 @@ const useStyles = () => {
94
96
  StyleSheet.create({
95
97
  bottomSheetColumnWrapper: {
96
98
  alignItems: 'center',
97
- justifyContent: 'center',
99
+ justifyContent: 'space-evenly',
98
100
  width: '100%',
99
- height: 54,
101
+ height: EMOJI_CELL_SIZE,
100
102
  },
101
103
  bottomSheetContentContainer: { paddingBottom: 16 },
102
104
  emojiContainer: {
@@ -18,6 +18,7 @@ import {
18
18
  StyleSheet,
19
19
  useWindowDimensions,
20
20
  View,
21
+ type ModalProps,
21
22
  } from 'react-native';
22
23
  import { Gesture, GestureDetector, GestureHandlerRootView } from 'react-native-gesture-handler';
23
24
  import Animated, {
@@ -43,9 +44,19 @@ import { useAnnounceOnShow } from '../../a11y/hooks/useAnnounceOnShow';
43
44
  import { useResolvedModalAccessibilityProps } from '../../a11y/hooks/useResolvedModalAccessibilityProps';
44
45
  import { BottomSheetProvider } from '../../contexts/bottomSheetContext/BottomSheetContext';
45
46
  import { useTheme } from '../../contexts/themeContext/ThemeContext';
46
- import { useStableCallback } from '../../hooks';
47
+ import { useScreenOrientation, useStableCallback } from '../../hooks';
47
48
  import { primitives } from '../../theme';
48
49
 
50
+ const supportedOrientations: ModalProps['supportedOrientations'] = [
51
+ 'portrait',
52
+ 'portrait-upside-down',
53
+ 'landscape',
54
+ 'landscape-left',
55
+ 'landscape-right',
56
+ ];
57
+
58
+ const LANDSCAPE_MAX_HEIGHT_RATIO = 0.9;
59
+
49
60
  export type BottomSheetModalProps = {
50
61
  /**
51
62
  * Function to call when the modal is closed or dismissed.
@@ -90,11 +101,16 @@ const BottomSheetModalInner = (props: PropsWithChildren<BottomSheetModalProps>)
90
101
  },
91
102
  } = useTheme();
92
103
  const styles = useStyles();
104
+ const isLandscape = useScreenOrientation() === 'landscape';
93
105
 
94
- const maxHeight = Math.max(
106
+ const availableHeight = Math.max(
95
107
  0,
96
108
  windowHeight - topInset - (Platform.OS === 'android' ? bottomInset + 16 : 0),
97
109
  );
110
+
111
+ const maxHeight = isLandscape
112
+ ? Math.round(availableHeight * LANDSCAPE_MAX_HEIGHT_RATIO)
113
+ : availableHeight;
98
114
  const fixedBaseHeight = Math.min(height, maxHeight);
99
115
 
100
116
  const contentHeight = useSharedValue<number | undefined>(undefined);
@@ -574,6 +590,7 @@ const BottomSheetModalInner = (props: PropsWithChildren<BottomSheetModalProps>)
574
590
  <Modal
575
591
  onDismiss={handleNativeModalDismiss}
576
592
  onRequestClose={onClose}
593
+ supportedOrientations={supportedOrientations}
577
594
  transparent
578
595
  visible={visible && !isDismissing}
579
596
  >
@@ -128,6 +128,15 @@ export type InputMessageInputContextValue = {
128
128
  options?: UpdateMessageOptions;
129
129
  }) => ReturnType<StreamChat['updateMessage']>;
130
130
 
131
+ /**
132
+ * Controls what happens when the attach button is pressed while the attachment picker is open.
133
+ * When true (default), the message input is focused, toggling from the picker to the keyboard.
134
+ * When false, the attachment picker is closed without focusing the input.
135
+ * @type boolean
136
+ * @default true
137
+ */
138
+ focusInputOnPickerClose: boolean;
139
+
131
140
  /** When false, CameraSelectorIcon will be hidden */
132
141
  hasCameraPicker: boolean;
133
142
 
@@ -18,6 +18,7 @@ export const useCreateMessageInputContext = ({
18
18
  compressImageQuality,
19
19
  createPollOptionGap,
20
20
  editMessage,
21
+ focusInputOnPickerClose,
21
22
  handleAttachButtonPress,
22
23
  hasCameraPicker,
23
24
  hasCommands,
@@ -61,6 +62,7 @@ export const useCreateMessageInputContext = ({
61
62
  compressImageQuality,
62
63
  createPollOptionGap,
63
64
  editMessage,
65
+ focusInputOnPickerClose,
64
66
  handleAttachButtonPress,
65
67
  hasCameraPicker,
66
68
  hasCommands,
@@ -15,6 +15,7 @@ export * from './useSelectedChannelState';
15
15
  export * from './useStreami18n';
16
16
  export * from './useViewport';
17
17
  export * from './useScreenDimensions';
18
+ export * from './useScreenOrientation';
18
19
  export * from './useStateStore';
19
20
  export * from './usePendingAttachmentUpload';
20
21
  export * from './useStableCallback';
@@ -0,0 +1,15 @@
1
+ import { useWindowDimensions } from 'react-native';
2
+
3
+ export type ScreenOrientation = 'landscape' | 'portrait';
4
+
5
+ /**
6
+ * A custom hook that derives the current screen orientation from the window
7
+ * dimensions. It updates automatically on device rotation via
8
+ * `useWindowDimensions`.
9
+ *
10
+ * @returns {ScreenOrientation} Either `'landscape'` or `'portrait'`.
11
+ */
12
+ export const useScreenOrientation = (): ScreenOrientation => {
13
+ const { height, width } = useWindowDimensions();
14
+ return width > height ? 'landscape' : 'portrait';
15
+ };
package/src/version.json CHANGED
@@ -1,3 +1,3 @@
1
1
  {
2
- "version": "9.6.0"
2
+ "version": "9.6.1-beta.2"
3
3
  }