diff --git a/docusaurus/docs/reactnative/basics/installation.mdx b/docusaurus/docs/reactnative/basics/installation.mdx index 00674dd744..3b81c0be1a 100644 --- a/docusaurus/docs/reactnative/basics/installation.mdx +++ b/docusaurus/docs/reactnative/basics/installation.mdx @@ -56,7 +56,7 @@ Stream Chat React Native SDK requires installing some peer dependencies to provi ```bash title="Terminal" -yarn add @react-native-camera-roll/camera-roll @react-native-community/netinfo @stream-io/flat-list-mvcp react-native-fs react-native-gesture-handler react-native-image-crop-picker react-native-image-resizer react-native-reanimated react-native-svg +yarn add @react-native-camera-roll/camera-roll @react-native-community/netinfo @stream-io/flat-list-mvcp react-native-fs react-native-gesture-handler react-native-image-resizer react-native-reanimated react-native-svg ``` @@ -64,7 +64,7 @@ yarn add @react-native-camera-roll/camera-roll @react-native-community/netinfo @ ```bash title="Terminal" -npx expo install @stream-io/flat-list-mvcp @react-native-community/netinfo expo-file-system expo-image-manipulator expo-image-picker expo-media-library react-native-gesture-handler react-native-reanimated react-native-svg +npx expo install @stream-io/flat-list-mvcp @react-native-community/netinfo expo-file-system expo-image-manipulator expo-media-library react-native-gesture-handler react-native-reanimated react-native-svg ``` @@ -87,7 +87,6 @@ values={[ - [`@stream-io/flat-list-mvcp`](https://github.com/GetStream/flat-list-mvcp) for bi-directional FlatList support. - [`react-native-fs`](https://github.com/itinance/react-native-fs) to perform file operations like save, delete, etc. - [`react-native-gesture-handler`](https://github.com/software-mansion/react-native-gesture-handler) to handle gestures within the SDK. -- [`react-native-image-crop-picker`](https://github.com/ivpusic/react-native-image-crop-picker) to capture images to attach them in the message. - [`react-native-image-resizer`](https://github.com/bamlab/react-native-image-resizer) to compress image uploads. - [`react-native-reanimated`](https://github.com/software-mansion/react-native-reanimated) to compress image uploads. - [`react-native-svg`](https://github.com/react-native-svg/react-native-svg) for SVG support. @@ -100,7 +99,6 @@ values={[ - [`@stream-io/flat-list-mvcp`](https://github.com/GetStream/flat-list-mvcp) for bi-directional FlatList support. - [`expo-file-system`](https://docs.expo.dev/versions/latest/sdk/filesystem/) to perform file operations like save, delete, etc. - [`react-native-gesture-handler`](https://github.com/software-mansion/react-native-gesture-handler) to handle gestures within the SDK. -- [`expo-image-picker`](https://docs.expo.dev/versions/latest/sdk/imagepicker/) to capture images to attach them in the message. - [`expo-image-manipulator`](https://docs.expo.dev/versions/latest/sdk/imagemanipulator/) to compress image uploads. - [`react-native-reanimated`](https://github.com/software-mansion/react-native-reanimated) to compress image uploads. - [`react-native-svg`](https://docs.expo.dev/versions/latest/sdk/svg/) for SVG support. @@ -110,6 +108,10 @@ values={[ ### Optional Dependencies +:::note +Starting from `v5.35.0` the `react-native-image-crop-picker` and `expo-image-picker` is no longer a required dependency. You can use it if you want to capture images to attach them in the message else feel free to uninstall it. +::: + There are a few optional dependencies that can be added to have more features within the SDK. +- [`react-native-image-crop-picker`](https://github.com/ivpusic/react-native-image-crop-picker) to capture images to attach them in the message. - [`react-native-video`](https://github.com/react-native-video/react-native-video) for Video and Audio playback support. - [`react-native-audio-recorder-player`](https://github.com/hyochan/react-native-audio-recorder-player) for Audio recording and async audio messages support. - [`react-native-share`](https://github.com/react-native-share/react-native-share) for Attachment sharing support. @@ -134,6 +137,7 @@ values={[ - [`expo-av`](https://docs.expo.dev/versions/latest/sdk/av/) for Video and Audio playback, recording and async audio messages support. +- [`expo-image-picker`](https://docs.expo.dev/versions/latest/sdk/imagepicker/) to capture images to attach them in the message. - [`expo-sharing`](https://docs.expo.dev/versions/latest/sdk/sharing/) for Attachments sharing support. - [`expo-haptics`](https://docs.expo.dev/versions/latest/sdk/haptics/) for user haptics support. - [`expo-clipboard`](https://docs.expo.dev/versions/latest/sdk/clipboard/) for Copy message support. diff --git a/docusaurus/docs/reactnative/common-content/ui-components/channel/props/has_camera_picker.mdx b/docusaurus/docs/reactnative/common-content/ui-components/channel/props/has_camera_picker.mdx new file mode 100644 index 0000000000..5896a258d5 --- /dev/null +++ b/docusaurus/docs/reactnative/common-content/ui-components/channel/props/has_camera_picker.mdx @@ -0,0 +1,5 @@ +Enable the file picker on the [`MessageInput`](../../../../ui-components/message-input.mdx) component. + +| Type | Default | +| ------- | ------- | +| Boolean | `true` | diff --git a/docusaurus/docs/reactnative/common-content/ui-components/channel/props/has_file_picker.mdx b/docusaurus/docs/reactnative/common-content/ui-components/channel/props/has_file_picker.mdx index a3b9425550..5896a258d5 100644 --- a/docusaurus/docs/reactnative/common-content/ui-components/channel/props/has_file_picker.mdx +++ b/docusaurus/docs/reactnative/common-content/ui-components/channel/props/has_file_picker.mdx @@ -2,4 +2,4 @@ Enable the file picker on the [`MessageInput`](../../../../ui-components/message | Type | Default | | ------- | ------- | -| boolean | true | +| Boolean | `true` | diff --git a/docusaurus/docs/reactnative/common-content/ui-components/channel/props/has_image_picker.mdx b/docusaurus/docs/reactnative/common-content/ui-components/channel/props/has_image_picker.mdx index e9689e1787..999c8f1ce5 100644 --- a/docusaurus/docs/reactnative/common-content/ui-components/channel/props/has_image_picker.mdx +++ b/docusaurus/docs/reactnative/common-content/ui-components/channel/props/has_image_picker.mdx @@ -2,4 +2,4 @@ Enable the image picker on the [`MessageInput`](../../../../ui-components/messag | Type | Default | | ------- | ------- | -| boolean | true | +| Boolean | `true` | diff --git a/docusaurus/docs/reactnative/contexts/message-input-context.mdx b/docusaurus/docs/reactnative/contexts/message-input-context.mdx index dba4f6f94d..0b1b4d3111 100644 --- a/docusaurus/docs/reactnative/contexts/message-input-context.mdx +++ b/docusaurus/docs/reactnative/contexts/message-input-context.mdx @@ -26,6 +26,7 @@ import DoDocUploadRequest from '../common-content/ui-components/channel/props/do import DoImageUploadRequest from '../common-content/ui-components/channel/props/do_image_upload_request.mdx'; import EmojiSearchIndex from '../common-content/ui-components/channel/props/emoji_search_index.mdx'; import FileUploadPreview from '../common-content/ui-components/channel/props/file_upload_preview.mdx'; +import HasCameraPicker from '../common-content/ui-components/channel/props/has_camera_picker.mdx'; import HasCommands from '../common-content/ui-components/channel/props/has_commands.mdx'; import HasFilePicker from '../common-content/ui-components/channel/props/has_file_picker.mdx'; import HasImagePicker from '../common-content/ui-components/channel/props/has_image_picker.mdx'; @@ -182,15 +183,19 @@ const { sendMessage, toggleAttachmentPicker } = useMessageInputContext(); +###
_forwarded from [Channel](../../core-components/channel#hascamerapicker)_ props
`hasCameraPicker` {#hascamerapicker} + + + ###
_forwarded from [Channel](../../core-components/channel#hascommands)_ props
`hasCommands` {#hascommands} -###
_forwarded from [Channel](../../core-components/channel#hasfilepicker)_ props
hasFilePicker {#hasfilepicker} +###
_forwarded from [Channel](../../core-components/channel#hasfilepicker)_ props
`hasFilePicker` {#hasfilepicker} -###
_forwarded from [Channel](../../core-components/channel#hasimagepicker)_ props
hasImagePicker {#hasimagepicker} +###
_forwarded from [Channel](../../core-components/channel#hasimagepicker)_ props
`hasImagePicker` {#hasimagepicker} diff --git a/docusaurus/docs/reactnative/core-components/channel.mdx b/docusaurus/docs/reactnative/core-components/channel.mdx index 9352e0f8f4..87fadbd253 100644 --- a/docusaurus/docs/reactnative/core-components/channel.mdx +++ b/docusaurus/docs/reactnative/core-components/channel.mdx @@ -74,6 +74,7 @@ import HandleQuotedReply from '../common-content/ui-components/channel/props/han import HandleReaction from '../common-content/ui-components/channel/props/handle_reaction.mdx'; import HandleRetry from '../common-content/ui-components/channel/props/handle_retry.mdx'; import HandleThreadReply from '../common-content/ui-components/channel/props/handle_thread_reply.mdx'; +import HasCameraPicker from '../common-content/ui-components/channel/props/has_camera_picker.mdx'; import HasCommands from '../common-content/ui-components/channel/props/has_commands.mdx'; import HasFilePicker from '../common-content/ui-components/channel/props/has_file_picker.mdx'; import HasImagePicker from '../common-content/ui-components/channel/props/has_image_picker.mdx'; @@ -524,6 +525,10 @@ The max allowable is 255, which when reached displays as `255+`. +### `hasCameraPicker` + + + ### `hasCommands` diff --git a/examples/SampleApp/ios/Podfile.lock b/examples/SampleApp/ios/Podfile.lock index 4beb2a4589..0ecce10855 100644 --- a/examples/SampleApp/ios/Podfile.lock +++ b/examples/SampleApp/ios/Podfile.lock @@ -1240,15 +1240,15 @@ PODS: - glog - RCT-Folly (= 2022.05.16.00) - React-Core - - RNImageCropPicker (0.39.0): + - RNImageCropPicker (0.41.2): - React-Core - React-RCTImage - - RNImageCropPicker/QBImagePickerController (= 0.39.0) - - TOCropViewController - - RNImageCropPicker/QBImagePickerController (0.39.0): + - RNImageCropPicker/QBImagePickerController (= 0.41.2) + - TOCropViewController (~> 2.7.4) + - RNImageCropPicker/QBImagePickerController (0.41.2): - React-Core - React-RCTImage - - TOCropViewController + - TOCropViewController (~> 2.7.4) - RNNotifee (7.8.2): - React-Core - RNNotifee/NotifeeCore (= 7.8.2) @@ -1276,7 +1276,7 @@ PODS: - libwebp (~> 1.0) - SDWebImage/Core (~> 5.10) - SocketRocket (0.6.1) - - TOCropViewController (2.6.1) + - TOCropViewController (2.7.4) - Yoga (1.14.0) DEPENDENCIES: @@ -1608,7 +1608,7 @@ SPEC CHECKSUMS: RNFBMessaging: 9b16c72d001787aca05e2fb997e5c979b821dbb4 RNFS: 4ac0f0ea233904cb798630b3c077808c06931688 RNGestureHandler: 2e3251b41d462552997c61afd680220d019fea65 - RNImageCropPicker: 14fe1c29298fb4018f3186f455c475ab107da332 + RNImageCropPicker: 771e2ca319d2cf92e04ebf334ece892ee9a6728f RNNotifee: 8e2d3df3f0e9ce8f5d1fe4c967431138190b6175 RNReactNativeHapticFeedback: afa5bf2794aecbb2dba2525329253da0d66656df RNReanimated: 440ca83ef0a79a3376455663fc4a01300e131240 @@ -1618,7 +1618,7 @@ SPEC CHECKSUMS: SDWebImage: a7f831e1a65eb5e285e3fb046a23fcfbf08e696d SDWebImageWebPCoder: 908b83b6adda48effe7667cd2b7f78c897e5111d SocketRocket: f32cd54efbe0f095c4d7594881e52619cfe80b17 - TOCropViewController: edfd4f25713d56905ad1e0b9f5be3fbe0f59c863 + TOCropViewController: 80b8985ad794298fb69d3341de183f33d1853654 Yoga: 9e6a04eacbd94f97d94577017e9f23b3ab41cf6c PODFILE CHECKSUM: 751ee2c534898a790da0a7dba7d623f1f21ae757 diff --git a/examples/SampleApp/package.json b/examples/SampleApp/package.json index b008442742..07f2f72b5a 100644 --- a/examples/SampleApp/package.json +++ b/examples/SampleApp/package.json @@ -42,7 +42,7 @@ "react-native-fs": "^2.18.0", "react-native-gesture-handler": "^2.14.0", "react-native-haptic-feedback": "2.0.3", - "react-native-image-crop-picker": "0.39.0", + "react-native-image-crop-picker": "^0.41.2", "react-native-image-resizer": "1.4.5", "react-native-markdown-package": "1.8.2", "react-native-quick-sqlite": "8.0.2", diff --git a/examples/SampleApp/yarn.lock b/examples/SampleApp/yarn.lock index 11d050a942..0c77c13a81 100644 --- a/examples/SampleApp/yarn.lock +++ b/examples/SampleApp/yarn.lock @@ -6203,10 +6203,10 @@ react-native-haptic-feedback@2.0.3: resolved "https://registry.yarnpkg.com/react-native-haptic-feedback/-/react-native-haptic-feedback-2.0.3.tgz#09133b2175503831c04798cb0dc63ae91e3959c1" integrity sha512-7+qvcxXZts/hA+HOOIFyM1x9m9fn/TJVSTgXaoQ8uT4gLc97IMvqHQ559tDmnlth+hHMzd3HRMpmRLWoKPL0DA== -react-native-image-crop-picker@0.39.0: - version "0.39.0" - resolved "https://registry.yarnpkg.com/react-native-image-crop-picker/-/react-native-image-crop-picker-0.39.0.tgz#9cb8e8ffb0e8ab06f7b3227cadf077169e225eba" - integrity sha512-4aANbQMrmU6zN/4b0rVBA7SbaZ3aa5JESm3Xk751sINybZMt1yz/9h95LkO7U0pbslHDo3ofXjG75PmQRP6a/w== +react-native-image-crop-picker@^0.41.2: + version "0.41.2" + resolved "https://registry.yarnpkg.com/react-native-image-crop-picker/-/react-native-image-crop-picker-0.41.2.tgz#824fa8fee8391fbb3e0b5ae2973221a2dff0cafb" + integrity sha512-GcDu/adXU/1y/MrxsbOfqcGRGWC2pTttt5VGy/jyRJ6GXfoC29fTQf8SG5kGtc5schSR6K+mKYO4uW6eJPljlQ== react-native-image-resizer@1.4.5: version "1.4.5" diff --git a/examples/TypeScriptMessaging/ios/Podfile.lock b/examples/TypeScriptMessaging/ios/Podfile.lock index a425bc4bab..f12cb8d8c9 100644 --- a/examples/TypeScriptMessaging/ios/Podfile.lock +++ b/examples/TypeScriptMessaging/ios/Podfile.lock @@ -1148,15 +1148,15 @@ PODS: - glog - RCT-Folly (= 2022.05.16.00) - React-Core - - RNImageCropPicker (0.39.0): + - RNImageCropPicker (0.41.2): - React-Core - React-RCTImage - - RNImageCropPicker/QBImagePickerController (= 0.39.0) - - TOCropViewController - - RNImageCropPicker/QBImagePickerController (0.39.0): + - RNImageCropPicker/QBImagePickerController (= 0.41.2) + - TOCropViewController (~> 2.7.4) + - RNImageCropPicker/QBImagePickerController (0.41.2): - React-Core - React-RCTImage - - TOCropViewController + - TOCropViewController (~> 2.7.4) - RNReactNativeHapticFeedback (2.0.3): - React-Core - RNReanimated (3.7.1): @@ -1173,7 +1173,7 @@ PODS: - RNSVG (14.1.0): - React-Core - SocketRocket (0.6.1) - - TOCropViewController (2.6.1) + - TOCropViewController (2.7.4) - Yoga (1.14.0) DEPENDENCIES: @@ -1497,14 +1497,14 @@ SPEC CHECKSUMS: RNCMaskedView: 0e1bc4bfa8365eba5fbbb71e07fbdc0555249489 RNFS: 4ac0f0ea233904cb798630b3c077808c06931688 RNGestureHandler: 67fb54b3e6ca338a8044e85cd6f340265aa41091 - RNImageCropPicker: 14fe1c29298fb4018f3186f455c475ab107da332 + RNImageCropPicker: 771e2ca319d2cf92e04ebf334ece892ee9a6728f RNReactNativeHapticFeedback: afa5bf2794aecbb2dba2525329253da0d66656df RNReanimated: 15a855719335a6b655a214531e86d806edfd49da RNScreens: 17e2f657f1b09a71ec3c821368a04acbb7ebcb46 RNShare: d82e10f6b7677f4b0048c23709bd04098d5aee6c RNSVG: ba3e7232f45e34b7b47e74472386cf4e1a676d0a SocketRocket: f32cd54efbe0f095c4d7594881e52619cfe80b17 - TOCropViewController: edfd4f25713d56905ad1e0b9f5be3fbe0f59c863 + TOCropViewController: 80b8985ad794298fb69d3341de183f33d1853654 Yoga: d17d2cc8105eed528474683b42e2ea310e1daf61 PODFILE CHECKSUM: 90406e1e85c82b37484f5d746afa45c0637bb4b3 diff --git a/examples/TypeScriptMessaging/package.json b/examples/TypeScriptMessaging/package.json index 144f1b3f1a..fa2978a261 100644 --- a/examples/TypeScriptMessaging/package.json +++ b/examples/TypeScriptMessaging/package.json @@ -26,7 +26,7 @@ "react-native-fs": "^2.18.0", "react-native-gesture-handler": "^2.14.0", "react-native-haptic-feedback": "^2.0.3", - "react-native-image-crop-picker": "^0.39.0", + "react-native-image-crop-picker": "^0.41.2", "react-native-image-resizer": "^1.4.5", "react-native-quick-sqlite": "^8.0.2", "react-native-reanimated": "^3.7.0", diff --git a/examples/TypeScriptMessaging/yarn.lock b/examples/TypeScriptMessaging/yarn.lock index 2ee4fc7e48..5101e89ff9 100644 --- a/examples/TypeScriptMessaging/yarn.lock +++ b/examples/TypeScriptMessaging/yarn.lock @@ -6325,10 +6325,10 @@ react-native-haptic-feedback@^2.0.3: resolved "https://registry.yarnpkg.com/react-native-haptic-feedback/-/react-native-haptic-feedback-2.0.3.tgz#09133b2175503831c04798cb0dc63ae91e3959c1" integrity sha512-7+qvcxXZts/hA+HOOIFyM1x9m9fn/TJVSTgXaoQ8uT4gLc97IMvqHQ559tDmnlth+hHMzd3HRMpmRLWoKPL0DA== -react-native-image-crop-picker@^0.39.0: - version "0.39.0" - resolved "https://registry.yarnpkg.com/react-native-image-crop-picker/-/react-native-image-crop-picker-0.39.0.tgz#9cb8e8ffb0e8ab06f7b3227cadf077169e225eba" - integrity sha512-4aANbQMrmU6zN/4b0rVBA7SbaZ3aa5JESm3Xk751sINybZMt1yz/9h95LkO7U0pbslHDo3ofXjG75PmQRP6a/w== +react-native-image-crop-picker@^0.41.2: + version "0.41.2" + resolved "https://registry.yarnpkg.com/react-native-image-crop-picker/-/react-native-image-crop-picker-0.41.2.tgz#824fa8fee8391fbb3e0b5ae2973221a2dff0cafb" + integrity sha512-GcDu/adXU/1y/MrxsbOfqcGRGWC2pTttt5VGy/jyRJ6GXfoC29fTQf8SG5kGtc5schSR6K+mKYO4uW6eJPljlQ== react-native-image-resizer@^1.4.5: version "1.4.5" diff --git a/package/expo-package/package.json b/package/expo-package/package.json index b67e3908df..47ba406119 100644 --- a/package/expo-package/package.json +++ b/package/expo-package/package.json @@ -21,7 +21,7 @@ "expo-file-system": "*", "expo-haptics": "*", "expo-image-manipulator": "*", - "expo-image-picker": ">=14.1.0", + "expo-image-picker": "*", "expo-media-library": "*", "expo-sharing": "*" }, @@ -35,6 +35,9 @@ "expo-document-picker": { "optional": true }, + "expo-image-picker": { + "optional": true + }, "expo-sharing": { "optional": true }, @@ -47,7 +50,6 @@ "expo": "^44.0.0", "expo-file-system": "^11.0.2", "expo-image-manipulator": "^9.1.0", - "expo-image-picker": "^14.1.1", "expo-media-library": "~15.2.3" }, "scripts": { diff --git a/package/expo-package/src/handlers/index.ts b/package/expo-package/src/handlers/index.ts index 2518c330db..6f43860e50 100644 --- a/package/expo-package/src/handlers/index.ts +++ b/package/expo-package/src/handlers/index.ts @@ -8,5 +8,4 @@ export * from './NetInfo'; export * from './oniOS14GalleryLibrarySelectionChange'; export * from './saveFile'; export * from './Sound'; -export * from './takePhoto'; export * from './Video'; diff --git a/package/expo-package/src/handlers/takePhoto.ts b/package/expo-package/src/handlers/takePhoto.ts deleted file mode 100644 index b30d351ef5..0000000000 --- a/package/expo-package/src/handlers/takePhoto.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { Image, Platform } from 'react-native'; - -import * as ImagePicker from 'expo-image-picker'; - -type Size = { - height?: number; - width?: number; -}; - -export const takePhoto = async ({ compressImageQuality = 1 }) => { - try { - const permissionCheck = await ImagePicker.getCameraPermissionsAsync(); - const canRequest = permissionCheck.canAskAgain; - let permissionGranted = permissionCheck.granted; - if (!permissionGranted) { - if (canRequest) { - const response = await ImagePicker.requestCameraPermissionsAsync(); - permissionGranted = response.granted; - } else { - return { askToOpenSettings: true, cancelled: true }; - } - } - - if (permissionGranted) { - const imagePickerSuccessResult = await ImagePicker.launchCameraAsync({ - quality: Math.min(Math.max(0, compressImageQuality), 1), - }); - const canceled = imagePickerSuccessResult.canceled; - const assets = imagePickerSuccessResult.assets; - // since we only support single photo upload for now we will only be focusing on 0'th element. - const photo = assets && assets[0]; - - if (canceled === false && photo && photo.height && photo.width && photo.uri) { - let size: Size = {}; - if (Platform.OS === 'android') { - // Height and width returned by ImagePicker are incorrect on Android. - // The issue is described in following github issue: - // https://github.com/ivpusic/react-native-image-crop-picker/issues/901 - // This we can't rely on them as it is, and we need to use Image.getSize - // to get accurate size. - const getSize = (): Promise => - new Promise((resolve) => { - Image.getSize(photo.uri, (width, height) => { - resolve({ height, width }); - }); - }); - - try { - const { height, width } = await getSize(); - size.height = height; - size.width = width; - } catch (e) { - console.warn('Error get image size of picture caputred from camera ', e); - } - } else { - size = { - height: photo.height, - width: photo.width, - }; - } - - return { - cancelled: false, - source: 'camera', - uri: photo.uri, - ...size, - }; - } - } - } catch (error) { - console.log(error); - } - return { cancelled: true }; -}; diff --git a/package/expo-package/src/index.js b/package/expo-package/src/index.js index 818c0a53a6..a2c609b186 100644 --- a/package/expo-package/src/index.js +++ b/package/expo-package/src/index.js @@ -13,7 +13,6 @@ import { oniOS14GalleryLibrarySelectionChange, saveFile, Sound, - takePhoto, Video, } from './handlers'; @@ -21,6 +20,7 @@ import { pickDocument, setClipboardString, shareImage, + takePhoto, triggerHaptic, } from './optionalDependencies'; diff --git a/package/expo-package/src/optionalDependencies/index.ts b/package/expo-package/src/optionalDependencies/index.ts index 1dcd987add..d2114a319a 100644 --- a/package/expo-package/src/optionalDependencies/index.ts +++ b/package/expo-package/src/optionalDependencies/index.ts @@ -3,3 +3,4 @@ export * from './shareImage'; export * from './pickDocument'; export * from './triggerHaptic'; export * from './Video'; +export * from './takePhoto'; diff --git a/package/expo-package/src/optionalDependencies/takePhoto.ts b/package/expo-package/src/optionalDependencies/takePhoto.ts new file mode 100644 index 0000000000..2fed2c85fa --- /dev/null +++ b/package/expo-package/src/optionalDependencies/takePhoto.ts @@ -0,0 +1,88 @@ +import { Image, Platform } from 'react-native'; + +let ImagePicker; + +try { + ImagePicker = require('expo-image-picker'); +} catch (e) { + // do nothing +} + +if (!ImagePicker) { + console.log( + 'expo-image-picker is not installed. Installing this package will enable campturing photos through the app, and thereby send it.', + ); +} + +type Size = { + height?: number; + width?: number; +}; + +export const takePhoto = ImagePicker + ? async ({ compressImageQuality = 1 }) => { + try { + const permissionCheck = await ImagePicker.getCameraPermissionsAsync(); + const canRequest = permissionCheck.canAskAgain; + let permissionGranted = permissionCheck.granted; + if (!permissionGranted) { + if (canRequest) { + const response = await ImagePicker.requestCameraPermissionsAsync(); + permissionGranted = response.granted; + } else { + return { askToOpenSettings: true, cancelled: true }; + } + } + + if (permissionGranted) { + const imagePickerSuccessResult = await ImagePicker.launchCameraAsync({ + quality: Math.min(Math.max(0, compressImageQuality), 1), + }); + const canceled = imagePickerSuccessResult.canceled; + const assets = imagePickerSuccessResult.assets; + // since we only support single photo upload for now we will only be focusing on 0'th element. + const photo = assets && assets[0]; + + if (canceled === false && photo && photo.height && photo.width && photo.uri) { + let size: Size = {}; + if (Platform.OS === 'android') { + // Height and width returned by ImagePicker are incorrect on Android. + // The issue is described in following github issue: + // https://github.com/ivpusic/react-native-image-crop-picker/issues/901 + // This we can't rely on them as it is, and we need to use Image.getSize + // to get accurate size. + const getSize = (): Promise => + new Promise((resolve) => { + Image.getSize(photo.uri, (width, height) => { + resolve({ height, width }); + }); + }); + + try { + const { height, width } = await getSize(); + size.height = height; + size.width = width; + } catch (e) { + console.warn('Error get image size of picture caputred from camera ', e); + } + } else { + size = { + height: photo.height, + width: photo.width, + }; + } + + return { + cancelled: false, + source: 'camera', + uri: photo.uri, + ...size, + }; + } + } + } catch (error) { + console.log(error); + } + return { cancelled: true }; + } + : null; diff --git a/package/expo-package/yarn.lock b/package/expo-package/yarn.lock index 1678f99bc7..95eb408586 100644 --- a/package/expo-package/yarn.lock +++ b/package/expo-package/yarn.lock @@ -1895,11 +1895,6 @@ expo-font@~10.0.5: dependencies: fontfaceobserver "^2.1.0" -expo-image-loader@~4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/expo-image-loader/-/expo-image-loader-4.1.1.tgz#efadbb17de1861106864820194900f336dd641b6" - integrity sha512-ciEHVokU0f6w0eTxdRxLCio6tskMsjxWIoV92+/ZD37qePUJYMfEphPhu1sruyvMBNR8/j5iyOvPFVGTfO8oxA== - expo-image-manipulator@^9.1.0: version "9.2.2" resolved "https://registry.yarnpkg.com/expo-image-manipulator/-/expo-image-manipulator-9.2.2.tgz#0fc7d2032972961c0a5fb49511d230fe0788fa6f" @@ -1907,13 +1902,6 @@ expo-image-manipulator@^9.1.0: dependencies: expo-modules-core "~0.2.0" -expo-image-picker@^14.1.1: - version "14.1.1" - resolved "https://registry.yarnpkg.com/expo-image-picker/-/expo-image-picker-14.1.1.tgz#181f1348ba6a43df7b87cee4a601d45c79b7c2d7" - integrity sha512-SvWtnkLW7jp5Ntvk3lVcRQmhFYja8psmiR7O6P/+7S6f4llt3vaFwb4I3+pUXqJxxpi7BHc2+95qOLf0SFOIag== - dependencies: - expo-image-loader "~4.1.0" - expo-keep-awake@~10.0.2: version "10.0.2" resolved "https://registry.yarnpkg.com/expo-keep-awake/-/expo-keep-awake-10.0.2.tgz#706bda839782bb3e8ad4cbe43bde471a56368813" @@ -2946,10 +2934,10 @@ stream-buffers@2.2.x: resolved "https://registry.yarnpkg.com/stream-buffers/-/stream-buffers-2.2.0.tgz#91d5f5130d1cef96dcfa7f726945188741d09ee4" integrity sha512-uyQK/mx5QjHun80FLJTfaWE7JtwfRMKBLkMne6udYOmvH0CawotVa7TfgYHzAnpphn4+TweIx1QKMnRIbipmUg== -stream-chat-react-native-core@5.33.0: - version "5.33.0" - resolved "https://registry.yarnpkg.com/stream-chat-react-native-core/-/stream-chat-react-native-core-5.33.0.tgz#14f04de90cbc8db011bab8db3fa84abe2dc2eaec" - integrity sha512-V9OJA9MrHzaCw5q16ZRbEktA1HamITbXPOkVZOjpDbb0OBcmedmOnD9C2NFIprc770lhllS/1MKBDr0GdQ9NXQ== +stream-chat-react-native-core@5.33.1: + version "5.33.1" + resolved "https://registry.yarnpkg.com/stream-chat-react-native-core/-/stream-chat-react-native-core-5.33.1.tgz#d9e7847469d3ffb6e7fd35fbb7b720f2e25d172e" + integrity sha512-TCDmChJe07cYyL3sErc6qycRFMA+HbflCKRGrFvVvpU0RdWJljaqiOo3avFSauciSnQxx9WxzTkMism8YsFHcQ== dependencies: "@gorhom/bottom-sheet" "4.4.8" dayjs "1.10.5" diff --git a/package/native-package/package.json b/package/native-package/package.json index 22f17ea81f..05d391dc61 100644 --- a/package/native-package/package.json +++ b/package/native-package/package.json @@ -15,17 +15,17 @@ }, "peerDependencies": { "@react-native-camera-roll/camera-roll": ">=5.0.0", - "@react-native-community/netinfo": ">=2.0.7", "@react-native-clipboard/clipboard": "^1.11.1", + "@react-native-community/netinfo": ">=2.0.7", "@stream-io/flat-list-mvcp": "^0.10.3", "react-native": ">=0.60.0", + "react-native-audio-recorder-player": ">=3.6.4", "react-native-document-picker": ">=9.0.1", "react-native-fs": ">=2.16.6", "react-native-haptic-feedback": ">=1.11.0", "react-native-image-crop-picker": ">=0.33.2", "react-native-image-resizer": ">=1.4.2", "react-native-share": ">=4.1.0", - "react-native-audio-recorder-player": ">=3.6.4", "react-native-video": ">=6.4.2" }, "peerDependenciesMeta": { @@ -41,6 +41,9 @@ "react-native-haptic-feedback": { "optional": true }, + "react-native-image-crop-picker": { + "optional": true + }, "react-native-audio-recorder-player": { "optional": true }, @@ -58,7 +61,6 @@ "@stream-io/flat-list-mvcp": "0.10.3", "react-native": ">=0.60.0", "react-native-fs": ">=2.16.6", - "react-native-image-crop-picker": "^0.38.0", "react-native-image-resizer": ">=1.4.2" } } diff --git a/package/native-package/src/handlers/index.ts b/package/native-package/src/handlers/index.ts index 9c0e249db7..7d999e46f5 100644 --- a/package/native-package/src/handlers/index.ts +++ b/package/native-package/src/handlers/index.ts @@ -4,7 +4,6 @@ export * from './getLocalAssetUri'; export * from './getPhotos'; export * from './NetInfo'; export * from './saveFile'; -export * from './takePhoto'; export * from './Sound'; export * from './Video'; export * from './oniOS14GalleryLibrarySelectionChange'; diff --git a/package/native-package/src/handlers/takePhoto.ts b/package/native-package/src/handlers/takePhoto.ts deleted file mode 100644 index 344b215291..0000000000 --- a/package/native-package/src/handlers/takePhoto.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { AppState, Image, PermissionsAndroid, Platform } from 'react-native'; -import ImagePicker from 'react-native-image-crop-picker'; - -export const takePhoto = async ({ compressImageQuality = Platform.OS === 'ios' ? 0.8 : 1 }) => { - if (Platform.OS === 'android') { - const cameraPermissions = await PermissionsAndroid.check(PermissionsAndroid.PERMISSIONS.CAMERA); - if (!cameraPermissions) { - const androidPermissionStatus = await PermissionsAndroid.request( - PermissionsAndroid.PERMISSIONS.CAMERA, - ); - if (androidPermissionStatus === PermissionsAndroid.RESULTS.DENIED) { - return { cancelled: true }; - } else if (androidPermissionStatus === PermissionsAndroid.RESULTS.NEVER_ASK_AGAIN) { - return { askToOpenSettings: true, cancelled: true }; - } - } - } - try { - const photo = await ImagePicker.openCamera({ - compressImageQuality: Math.min(Math.max(0, compressImageQuality), 1), - }); - if (photo.height && photo.width && photo.path) { - let size: { height?: number; width?: number } = {}; - if (Platform.OS === 'android') { - // Height and width returned by ImagePicker are incorrect on Android. - // The issue is described in following github issue: - // https://github.com/ivpusic/react-native-image-crop-picker/issues/901 - // This we can't rely on them as it is, and we need to use Image.getSize - // to get accurate size. - const getSize = (): Promise<{ height: number; width: number }> => - new Promise((resolve) => { - Image.getSize(photo.path, (width, height) => { - resolve({ height, width }); - }); - }); - - try { - const { height, width } = await getSize(); - size.height = height; - size.width = width; - } catch (e) { - // do nothing - console.warn('Error get image size of picture caputred from camera ', e); - } - } else { - size = { - height: photo.height, - width: photo.width, - }; - } - return { - cancelled: false, - source: 'camera', - uri: photo.path, - ...size, - }; - } - } catch (e: unknown) { - if (e instanceof Error) { - // on iOS: if it was in inactive state, then the user had just denied the permissions - if (Platform.OS === 'ios' && AppState.currentState === 'active') { - const cameraPermissionDeniedMsg = 'User did not grant camera permission.'; - // Open settings when the user did not allow camera permissions - if (e.message === cameraPermissionDeniedMsg) { - return { askToOpenSettings: true, cancelled: true }; - } - } - } - } - - return { cancelled: true }; -}; diff --git a/package/native-package/src/index.js b/package/native-package/src/index.js index 1c10124c3e..5dd66d0ca4 100644 --- a/package/native-package/src/index.js +++ b/package/native-package/src/index.js @@ -13,7 +13,6 @@ import { oniOS14GalleryLibrarySelectionChange, saveFile, Sound, - takePhoto, Video, } from './handlers'; @@ -22,6 +21,7 @@ import { pickDocument, setClipboardString, shareImage, + takePhoto, triggerHaptic, } from './optionalDependencies'; diff --git a/package/native-package/src/optionalDependencies/index.ts b/package/native-package/src/optionalDependencies/index.ts index fbb2228b1f..c4bf0a1242 100644 --- a/package/native-package/src/optionalDependencies/index.ts +++ b/package/native-package/src/optionalDependencies/index.ts @@ -4,3 +4,4 @@ export * from './Video'; export * from './triggerHaptic'; export * from './setClipboardString'; export * from './pickDocument'; +export * from './takePhoto'; diff --git a/package/native-package/src/optionalDependencies/takePhoto.ts b/package/native-package/src/optionalDependencies/takePhoto.ts new file mode 100644 index 0000000000..33b2e694b8 --- /dev/null +++ b/package/native-package/src/optionalDependencies/takePhoto.ts @@ -0,0 +1,83 @@ +import { AppState, Image, PermissionsAndroid, Platform } from 'react-native'; + +let ImagePicker; + +try { + ImagePicker = require('react-native-image-crop-picker').default; +} catch (e) { + console.log('react-native-image-crop-picker is not installed'); +} + +export const takePhoto = ImagePicker + ? async ({ compressImageQuality = Platform.OS === 'ios' ? 0.8 : 1 }) => { + if (Platform.OS === 'android') { + const cameraPermissions = await PermissionsAndroid.check( + PermissionsAndroid.PERMISSIONS.CAMERA, + ); + if (!cameraPermissions) { + const androidPermissionStatus = await PermissionsAndroid.request( + PermissionsAndroid.PERMISSIONS.CAMERA, + ); + if (androidPermissionStatus === PermissionsAndroid.RESULTS.DENIED) { + return { cancelled: true }; + } else if (androidPermissionStatus === PermissionsAndroid.RESULTS.NEVER_ASK_AGAIN) { + return { askToOpenSettings: true, cancelled: true }; + } + } + } + try { + const photo = await ImagePicker.openCamera({ + compressImageQuality: Math.min(Math.max(0, compressImageQuality), 1), + }); + if (photo.height && photo.width && photo.path) { + let size: { height?: number; width?: number } = {}; + if (Platform.OS === 'android') { + // Height and width returned by ImagePicker are incorrect on Android. + // The issue is described in following github issue: + // https://github.com/ivpusic/react-native-image-crop-picker/issues/901 + // This we can't rely on them as it is, and we need to use Image.getSize + // to get accurate size. + const getSize = (): Promise<{ height: number; width: number }> => + new Promise((resolve) => { + Image.getSize(photo.path, (width, height) => { + resolve({ height, width }); + }); + }); + + try { + const { height, width } = await getSize(); + size.height = height; + size.width = width; + } catch (e) { + // do nothing + console.warn('Error get image size of picture caputred from camera ', e); + } + } else { + size = { + height: photo.height, + width: photo.width, + }; + } + return { + cancelled: false, + source: 'camera', + uri: photo.path, + ...size, + }; + } + } catch (e: unknown) { + if (e instanceof Error) { + // on iOS: if it was in inactive state, then the user had just denied the permissions + if (Platform.OS === 'ios' && AppState.currentState === 'active') { + const cameraPermissionDeniedMsg = 'User did not grant camera permission.'; + // Open settings when the user did not allow camera permissions + if (e.message === cameraPermissionDeniedMsg) { + return { askToOpenSettings: true, cancelled: true }; + } + } + } + } + + return { cancelled: true }; + } + : null; diff --git a/package/native-package/yarn.lock b/package/native-package/yarn.lock index 7dd2c23696..bfe6875815 100644 --- a/package/native-package/yarn.lock +++ b/package/native-package/yarn.lock @@ -3743,11 +3743,6 @@ react-native-gradle-plugin@^0.71.18: resolved "https://registry.yarnpkg.com/react-native-gradle-plugin/-/react-native-gradle-plugin-0.71.18.tgz#20ef199bc85be32e45bb6cc069ec2e7dcb1a74a6" integrity sha512-7F6bD7B8Xsn3JllxcwHhFcsl9aHIig47+3eN4IHFNqfLhZr++3ElDrcqfMzugM+niWbaMi7bJ0kAkAL8eCpdWg== -react-native-image-crop-picker@^0.38.0: - version "0.38.1" - resolved "https://registry.yarnpkg.com/react-native-image-crop-picker/-/react-native-image-crop-picker-0.38.1.tgz#5973b4a8b55835b987e6be2064de411e849ac005" - integrity sha512-cF5UQnWplzHCeiCO+aiGS/0VomWaLmFf3nSsgTMPfY+8+99h8N/eHQvVdSF7RsGw50B8394wGeGyqHjjp8YRWw== - react-native-image-resizer@>=1.4.2: version "1.4.5" resolved "https://registry.yarnpkg.com/react-native-image-resizer/-/react-native-image-resizer-1.4.5.tgz#5a520aa8baa07638b1894a1d87d4d9a0945c8d58" diff --git a/package/src/components/AttachmentPicker/components/AttachmentPickerSelectionBar.tsx b/package/src/components/AttachmentPicker/components/AttachmentPickerSelectionBar.tsx index 12419744ff..c0dff1423a 100644 --- a/package/src/components/AttachmentPicker/components/AttachmentPickerSelectionBar.tsx +++ b/package/src/components/AttachmentPicker/components/AttachmentPickerSelectionBar.tsx @@ -32,7 +32,8 @@ export const AttachmentPickerSelectionBar = () => { } = useAttachmentPickerContext(); const { t } = useTranslationContext(); - const { compressImageQuality, hasFilePicker, imageUploads, pickFile } = useMessageInputContext(); + const { compressImageQuality, hasCameraPicker, hasFilePicker, imageUploads, pickFile } = + useMessageInputContext(); const { theme: { @@ -88,7 +89,7 @@ export const AttachmentPickerSelectionBar = () => { /> - {hasFilePicker && ( + {hasFilePicker ? ( { /> - )} - - - - - + ) : null} + {hasCameraPicker ? ( + + + + + + ) : null} ); }; diff --git a/package/src/components/Channel/Channel.tsx b/package/src/components/Channel/Channel.tsx index e8bebdb225..9ee2f7152f 100644 --- a/package/src/components/Channel/Channel.tsx +++ b/package/src/components/Channel/Channel.tsx @@ -75,7 +75,7 @@ import { ThumbsUpReaction, WutReaction, } from '../../icons'; -import { FlatList as FlatListDefault, pickDocument } from '../../native'; +import { FlatList as FlatListDefault, isImagePickerAvailable, pickDocument } from '../../native'; import * as dbApi from '../../store/apis'; import type { DefaultStreamChatGenerics } from '../../types/types'; import { addReactionToLocalState } from '../../utils/addReactionToLocalState'; @@ -491,6 +491,7 @@ const ChannelWithContext = < handleReaction, handleRetry, handleThreadReply, + hasCameraPicker = isImagePickerAvailable(), hasCommands = true, // If pickDocument isn't available, default to hiding the file picker hasFilePicker = pickDocument !== null, @@ -2226,6 +2227,7 @@ const ChannelWithContext = < editMessage, emojiSearchIndex, FileUploadPreview, + hasCameraPicker, hasCommands, hasFilePicker, hasImagePicker, diff --git a/package/src/components/Channel/hooks/useCreateInputMessageInputContext.ts b/package/src/components/Channel/hooks/useCreateInputMessageInputContext.ts index 73722b706d..39235f0008 100644 --- a/package/src/components/Channel/hooks/useCreateInputMessageInputContext.ts +++ b/package/src/components/Channel/hooks/useCreateInputMessageInputContext.ts @@ -34,6 +34,7 @@ export const useCreateInputMessageInputContext = < editMessage, emojiSearchIndex, FileUploadPreview, + hasCameraPicker, hasCommands, hasFilePicker, hasImagePicker, @@ -103,6 +104,7 @@ export const useCreateInputMessageInputContext = < editMessage, emojiSearchIndex, FileUploadPreview, + hasCameraPicker, hasCommands, hasFilePicker, hasImagePicker, diff --git a/package/src/components/MessageInput/InputButtons.tsx b/package/src/components/MessageInput/InputButtons.tsx index 9d24b12168..f4752e45b4 100644 --- a/package/src/components/MessageInput/InputButtons.tsx +++ b/package/src/components/MessageInput/InputButtons.tsx @@ -25,6 +25,7 @@ export type InputButtonsWithContextProps< | 'AttachButton' | 'CommandsButton' | 'giphyActive' + | 'hasCameraPicker' | 'hasCommands' | 'hasFilePicker' | 'hasImagePicker' @@ -46,6 +47,7 @@ export const InputButtonsWithContext = < AttachButton, CommandsButton, giphyActive, + hasCameraPicker, hasCommands, hasFilePicker, hasImagePicker, @@ -69,11 +71,11 @@ export const InputButtonsWithContext = < return null; } - return !showMoreOptions && (hasImagePicker || hasFilePicker) && hasCommands ? ( + return !showMoreOptions && (hasCameraPicker || hasImagePicker || hasFilePicker) && hasCommands ? ( setShowMoreOptions(true)} /> ) : ( <> - {(hasImagePicker || hasFilePicker) && ownCapabilities.uploadFile && ( + {(hasCameraPicker || hasImagePicker || hasFilePicker) && ownCapabilities.uploadFile && ( @@ -95,6 +97,7 @@ const areEqual = { const { giphyActive: prevGiphyActive, + hasCameraPicker: prevHasCameraPicker, hasCommands: prevHasCommands, hasFilePicker: prevHasFilePicker, hasImagePicker: prevHasImagePicker, @@ -105,6 +108,7 @@ const areEqual = { return { isAudioPackageAvailable: jest.fn(() => true), + isImagePickerAvailable: jest.fn(() => true), NetInfo: { addEventListener: jest.fn(), fetch: jest.fn(), diff --git a/package/src/contexts/messageInputContext/MessageInputContext.tsx b/package/src/contexts/messageInputContext/MessageInputContext.tsx index 385e9146da..a8b1dbd0ee 100644 --- a/package/src/contexts/messageInputContext/MessageInputContext.tsx +++ b/package/src/contexts/messageInputContext/MessageInputContext.tsx @@ -307,6 +307,8 @@ export type InputMessageInputContextValue< */ FileUploadPreview: React.ComponentType>; + /** When false, CameraSelectorIcon will be hidden */ + hasCameraPicker: boolean; /** When false, CommandsButton will be hidden */ hasCommands: boolean; /** When false, FileSelectorIcon will be hidden */ diff --git a/package/src/contexts/messageInputContext/hooks/useCreateMessageInputContext.ts b/package/src/contexts/messageInputContext/hooks/useCreateMessageInputContext.ts index a09a8737d7..32dc448416 100644 --- a/package/src/contexts/messageInputContext/hooks/useCreateMessageInputContext.ts +++ b/package/src/contexts/messageInputContext/hooks/useCreateMessageInputContext.ts @@ -40,6 +40,7 @@ export const useCreateMessageInputContext = < FileUploadPreview, fileUploads, giphyActive, + hasCameraPicker, hasCommands, hasFilePicker, hasImagePicker, @@ -160,6 +161,7 @@ export const useCreateMessageInputContext = < FileUploadPreview, fileUploads, giphyActive, + hasCameraPicker, hasCommands, hasFilePicker, hasImagePicker, diff --git a/package/src/native.ts b/package/src/native.ts index 44ee874048..27311ce8f3 100644 --- a/package/src/native.ts +++ b/package/src/native.ts @@ -360,11 +360,11 @@ export const registerNativeHandlers = (handlers: Handlers) => { Sound = handlers.Sound; } - if (handlers.takePhoto) { + if (handlers.takePhoto !== undefined) { takePhoto = handlers.takePhoto; } - if (handlers.triggerHaptic) { + if (handlers.triggerHaptic !== undefined) { triggerHaptic = handlers.triggerHaptic; } @@ -377,6 +377,7 @@ export const registerNativeHandlers = (handlers: Handlers) => { } }; +export const isImagePickerAvailable = () => !!takePhoto; export const isVideoPackageAvailable = () => !!Video; export const isAudioPackageAvailable = () => !!Sound.Player || !!Sound.initializeSound; export const isRecordingPackageAvailable = () => !!Audio;