stream-chat-react-native 5.4.0-beta.2 → 5.4.0-beta.4
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/package.json +9 -2
- package/src/handlers/NetInfo.ts +43 -0
- package/src/handlers/Sound.tsx +26 -0
- package/src/handlers/Video.tsx +22 -0
- package/src/handlers/compressImage.ts +34 -0
- package/src/handlers/deleteFile.ts +11 -0
- package/src/handlers/getLocalAssetUri.ts +10 -0
- package/src/handlers/getPhotos.ts +39 -0
- package/src/handlers/index.ts +11 -0
- package/src/handlers/pickDocument.ts +27 -0
- package/src/handlers/saveFile.ts +11 -0
- package/src/handlers/shareImage.ts +51 -0
- package/src/handlers/takePhoto.ts +47 -0
- package/src/handlers/triggerHaptic.ts +46 -0
- package/src/index.js +27 -289
- package/src/optionalDependencies/Video.ts +20 -1
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stream-chat-react-native",
|
|
3
3
|
"description": "The official React Native SDK for Stream Chat, a service for building chat applications",
|
|
4
|
-
"version": "5.4.0-beta.
|
|
4
|
+
"version": "5.4.0-beta.4",
|
|
5
5
|
"author": {
|
|
6
6
|
"company": "Stream.io Inc",
|
|
7
7
|
"name": "Stream.io Inc"
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"types": "types/index.d.ts",
|
|
12
12
|
"dependencies": {
|
|
13
13
|
"es6-symbol": "^3.1.3",
|
|
14
|
-
"stream-chat-react-native-core": "5.4.0-beta.
|
|
14
|
+
"stream-chat-react-native-core": "5.4.0-beta.4"
|
|
15
15
|
},
|
|
16
16
|
"peerDependencies": {
|
|
17
17
|
"@react-native-camera-roll/camera-roll": "^5.0.0",
|
|
@@ -28,5 +28,12 @@
|
|
|
28
28
|
"scripts": {
|
|
29
29
|
"prepack": " cp ../../README.md .",
|
|
30
30
|
"postpack": "rm README.md"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@react-native-camera-roll/camera-roll": "^5.0.2",
|
|
34
|
+
"@types/react-native-share": "^3.3.3",
|
|
35
|
+
"react-native-document-picker": "^8.1.1",
|
|
36
|
+
"react-native-haptic-feedback": "^1.14.0",
|
|
37
|
+
"react-native-image-crop-picker": "^0.38.0"
|
|
31
38
|
}
|
|
32
39
|
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { default as OriginalNetInfo, NetInfoState } from '@react-native-community/netinfo';
|
|
2
|
+
|
|
3
|
+
export const NetInfo = {
|
|
4
|
+
addEventListener(listener) {
|
|
5
|
+
let unsubscribe;
|
|
6
|
+
// For NetInfo >= 3.x.x
|
|
7
|
+
if (OriginalNetInfo.fetch && typeof OriginalNetInfo.fetch === 'function') {
|
|
8
|
+
unsubscribe = OriginalNetInfo.addEventListener(({ isConnected, isInternetReachable }) => {
|
|
9
|
+
// Initialize with truthy value when internetReachable is still loading
|
|
10
|
+
// if it resolves to false, listener is triggered with false value and network
|
|
11
|
+
// status is updated
|
|
12
|
+
listener(isInternetReachable === null ? isConnected : isConnected && isInternetReachable);
|
|
13
|
+
});
|
|
14
|
+
return unsubscribe;
|
|
15
|
+
} else {
|
|
16
|
+
// For NetInfo < 3.x.x
|
|
17
|
+
unsubscribe = OriginalNetInfo.addEventListener('connectionChange', () => {
|
|
18
|
+
// @ts-ignore
|
|
19
|
+
OriginalNetInfo.isConnected.fetch().then((isConnected: NetInfoState) => {
|
|
20
|
+
listener(isConnected);
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
return unsubscribe.remove;
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
fetch() {
|
|
28
|
+
return new Promise((resolve, reject) => {
|
|
29
|
+
// For NetInfo >= 3.x.x
|
|
30
|
+
if (OriginalNetInfo.fetch && typeof OriginalNetInfo.fetch === 'function') {
|
|
31
|
+
OriginalNetInfo.fetch().then(({ isConnected }) => {
|
|
32
|
+
resolve(isConnected);
|
|
33
|
+
}, reject);
|
|
34
|
+
} else {
|
|
35
|
+
// For NetInfo < 3.x.x
|
|
36
|
+
// @ts-ignore
|
|
37
|
+
OriginalNetInfo.isConnected.fetch().then((isConnected: NetInfoState) => {
|
|
38
|
+
resolve(isConnected);
|
|
39
|
+
}, reject);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
},
|
|
43
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import AudioVideoPlayer from '../optionalDependencies/Video';
|
|
2
|
+
|
|
3
|
+
export const Sound = {
|
|
4
|
+
initializeSound: null,
|
|
5
|
+
// eslint-disable-next-line react/display-name
|
|
6
|
+
Player: AudioVideoPlayer
|
|
7
|
+
? ({ onBuffer, onEnd, onLoad, onProgress, paused, soundRef, style, uri }) => (
|
|
8
|
+
<AudioVideoPlayer
|
|
9
|
+
audioOnly={true}
|
|
10
|
+
onBuffer={onBuffer}
|
|
11
|
+
onEnd={onEnd}
|
|
12
|
+
onError={(error: Error) => {
|
|
13
|
+
console.log(error);
|
|
14
|
+
}}
|
|
15
|
+
onLoad={onLoad}
|
|
16
|
+
onProgress={onProgress}
|
|
17
|
+
paused={paused}
|
|
18
|
+
ref={soundRef}
|
|
19
|
+
source={{
|
|
20
|
+
uri,
|
|
21
|
+
}}
|
|
22
|
+
style={style}
|
|
23
|
+
/>
|
|
24
|
+
)
|
|
25
|
+
: null,
|
|
26
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import AudioVideoPlayer from '../optionalDependencies/Video';
|
|
2
|
+
export const Video = AudioVideoPlayer
|
|
3
|
+
? ({ onBuffer, onEnd, onLoad, onProgress, paused, repeat, style, uri, videoRef }) => (
|
|
4
|
+
<AudioVideoPlayer
|
|
5
|
+
ignoreSilentSwitch={'ignore'}
|
|
6
|
+
onBuffer={onBuffer}
|
|
7
|
+
onEnd={onEnd}
|
|
8
|
+
onError={(error) => {
|
|
9
|
+
console.error(error);
|
|
10
|
+
}}
|
|
11
|
+
onLoad={onLoad}
|
|
12
|
+
onProgress={onProgress}
|
|
13
|
+
paused={paused}
|
|
14
|
+
ref={videoRef}
|
|
15
|
+
repeat={repeat}
|
|
16
|
+
source={{
|
|
17
|
+
uri,
|
|
18
|
+
}}
|
|
19
|
+
style={style}
|
|
20
|
+
/>
|
|
21
|
+
)
|
|
22
|
+
: null;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// @ts-ignore this module does not have a type declaration
|
|
2
|
+
import ImageResizer from 'react-native-image-resizer';
|
|
3
|
+
|
|
4
|
+
type CompressImageParams = {
|
|
5
|
+
compressImageQuality: number;
|
|
6
|
+
height: number;
|
|
7
|
+
uri: string;
|
|
8
|
+
width: number;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const compressImage = async ({
|
|
12
|
+
compressImageQuality = 1,
|
|
13
|
+
height,
|
|
14
|
+
uri,
|
|
15
|
+
width,
|
|
16
|
+
}: CompressImageParams) => {
|
|
17
|
+
try {
|
|
18
|
+
const { uri: compressedUri } = await ImageResizer.createResizedImage(
|
|
19
|
+
uri,
|
|
20
|
+
height,
|
|
21
|
+
width,
|
|
22
|
+
'JPEG',
|
|
23
|
+
Math.min(Math.max(0, compressImageQuality), 1) * 100,
|
|
24
|
+
0,
|
|
25
|
+
undefined,
|
|
26
|
+
false,
|
|
27
|
+
'cover',
|
|
28
|
+
);
|
|
29
|
+
return compressedUri;
|
|
30
|
+
} catch (error) {
|
|
31
|
+
console.log(error);
|
|
32
|
+
return uri;
|
|
33
|
+
}
|
|
34
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { CameraRoll } from '@react-native-camera-roll/camera-roll';
|
|
2
|
+
|
|
3
|
+
export const getLocalAssetUri = async (remoteUri: string) => {
|
|
4
|
+
try {
|
|
5
|
+
const localUri = await CameraRoll.save(remoteUri);
|
|
6
|
+
return localUri;
|
|
7
|
+
} catch {
|
|
8
|
+
throw new Error('getLocalAssetUri Error');
|
|
9
|
+
}
|
|
10
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { PermissionsAndroid, Platform } from 'react-native';
|
|
2
|
+
import { CameraRoll, GetPhotosParams } from '@react-native-camera-roll/camera-roll';
|
|
3
|
+
|
|
4
|
+
export const getPhotos = async ({ after, first }: Pick<GetPhotosParams, 'after' | 'first'>) => {
|
|
5
|
+
try {
|
|
6
|
+
if (Platform.OS === 'android') {
|
|
7
|
+
const readExternalStoragePermissionAndroid =
|
|
8
|
+
PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE;
|
|
9
|
+
const hasPermission = await PermissionsAndroid.check(readExternalStoragePermissionAndroid);
|
|
10
|
+
if (!hasPermission) {
|
|
11
|
+
const granted = await PermissionsAndroid.request(readExternalStoragePermissionAndroid, {
|
|
12
|
+
buttonNegative: 'Deny',
|
|
13
|
+
buttonNeutral: 'Ask Me Later',
|
|
14
|
+
buttonPositive: 'Allow',
|
|
15
|
+
message: 'Permissions are required to access and share photos.',
|
|
16
|
+
title: 'Photos Access',
|
|
17
|
+
});
|
|
18
|
+
if (granted !== PermissionsAndroid.RESULTS.GRANTED) {
|
|
19
|
+
throw new Error('getPhotos Error');
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
const results = await CameraRoll.getPhotos({
|
|
24
|
+
after,
|
|
25
|
+
assetType: 'All',
|
|
26
|
+
first,
|
|
27
|
+
include: ['fileSize', 'filename', 'imageSize', 'playableDuration'],
|
|
28
|
+
});
|
|
29
|
+
const assets = results.edges.map((edge) => ({
|
|
30
|
+
...edge.node.image,
|
|
31
|
+
source: 'picker',
|
|
32
|
+
}));
|
|
33
|
+
const hasNextPage = results.page_info.has_next_page;
|
|
34
|
+
const endCursor = results.page_info.end_cursor;
|
|
35
|
+
return { assets, endCursor, hasNextPage };
|
|
36
|
+
} catch (_error) {
|
|
37
|
+
throw new Error('getPhotos Error');
|
|
38
|
+
}
|
|
39
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export * from './shareImage';
|
|
2
|
+
export * from './deleteFile';
|
|
3
|
+
export * from './compressImage';
|
|
4
|
+
export * from './getLocalAssetUri';
|
|
5
|
+
export * from './getPhotos';
|
|
6
|
+
export * from './NetInfo';
|
|
7
|
+
export * from './saveFile';
|
|
8
|
+
export * from './takePhoto';
|
|
9
|
+
export * from './triggerHaptic';
|
|
10
|
+
export * from './Sound';
|
|
11
|
+
export * from './Video';
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import DocumentPicker from 'react-native-document-picker';
|
|
2
|
+
|
|
3
|
+
export const pickDocument = async ({ maxNumberOfFiles }: { maxNumberOfFiles: number }) => {
|
|
4
|
+
try {
|
|
5
|
+
let res = await DocumentPicker.pickMultiple({
|
|
6
|
+
type: [DocumentPicker.types.allFiles],
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
if (maxNumberOfFiles && res.length > maxNumberOfFiles) {
|
|
10
|
+
res = res.slice(0, maxNumberOfFiles);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
return {
|
|
14
|
+
cancelled: false,
|
|
15
|
+
docs: res.map(({ name, size, type, uri }) => ({
|
|
16
|
+
name,
|
|
17
|
+
size,
|
|
18
|
+
type,
|
|
19
|
+
uri,
|
|
20
|
+
})),
|
|
21
|
+
};
|
|
22
|
+
} catch (err) {
|
|
23
|
+
return {
|
|
24
|
+
cancelled: true,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import RNFS from 'react-native-fs';
|
|
2
|
+
|
|
3
|
+
export const saveFile = async ({ fileName, fromUrl }: { fileName: string; fromUrl: string }) => {
|
|
4
|
+
try {
|
|
5
|
+
const path = RNFS.CachesDirectoryPath + '/' + encodeURIComponent(fileName);
|
|
6
|
+
await RNFS.downloadFile({ fromUrl, toFile: path }).promise;
|
|
7
|
+
return 'file://' + path;
|
|
8
|
+
} catch (error) {
|
|
9
|
+
throw new Error('Downloading image failed...');
|
|
10
|
+
}
|
|
11
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import RNFS from 'react-native-fs';
|
|
2
|
+
import { Platform } from 'react-native';
|
|
3
|
+
|
|
4
|
+
let RNShare;
|
|
5
|
+
|
|
6
|
+
try {
|
|
7
|
+
RNShare = require('react-native-share').default;
|
|
8
|
+
} catch (e) {
|
|
9
|
+
console.log('react-native-share is not installed');
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const shareImage = RNShare
|
|
13
|
+
? async ({ type, url }) => {
|
|
14
|
+
try {
|
|
15
|
+
const base64Image = await RNFS.readFile(url, 'base64');
|
|
16
|
+
const base64Url = `data:${type};base64,${base64Image}`;
|
|
17
|
+
await RNShare.open({
|
|
18
|
+
activityItemSources:
|
|
19
|
+
Platform.OS === 'ios'
|
|
20
|
+
? [
|
|
21
|
+
{
|
|
22
|
+
item: {
|
|
23
|
+
default: {
|
|
24
|
+
content: url,
|
|
25
|
+
type: 'url',
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
linkMetadata: {
|
|
29
|
+
icon: url,
|
|
30
|
+
},
|
|
31
|
+
placeholderItem: {
|
|
32
|
+
content: url,
|
|
33
|
+
type: 'url',
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
]
|
|
37
|
+
: undefined,
|
|
38
|
+
// react-native-share has a typing issue, where their docs confirm that
|
|
39
|
+
// this property should be an array of strings, but the type is a string
|
|
40
|
+
// in the @types/react-native-share package.
|
|
41
|
+
excludedActivityTypes: [] as unknown as string,
|
|
42
|
+
failOnCancel: false,
|
|
43
|
+
type,
|
|
44
|
+
url: Platform.OS === 'android' ? base64Url : undefined,
|
|
45
|
+
});
|
|
46
|
+
return true;
|
|
47
|
+
} catch (error) {
|
|
48
|
+
throw new Error('Sharing failed...');
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
: null;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { Image, Platform } from 'react-native';
|
|
2
|
+
import ImagePicker from 'react-native-image-crop-picker';
|
|
3
|
+
|
|
4
|
+
export const takePhoto = async ({ compressImageQuality = Platform.OS === 'ios' ? 0.8 : 1 }) => {
|
|
5
|
+
const photo = await ImagePicker.openCamera({
|
|
6
|
+
compressImageQuality: Math.min(Math.max(0, compressImageQuality), 1),
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
if (photo.height && photo.width && photo.path) {
|
|
10
|
+
let size: { height?: number; width?: number } = {};
|
|
11
|
+
if (Platform.OS === 'android') {
|
|
12
|
+
// Height and width returned by ImagePicker are incorrect on Android.
|
|
13
|
+
// The issue is described in following github issue:
|
|
14
|
+
// https://github.com/ivpusic/react-native-image-crop-picker/issues/901
|
|
15
|
+
// This we can't rely on them as it is, and we need to use Image.getSize
|
|
16
|
+
// to get accurate size.
|
|
17
|
+
const getSize = (): Promise<{ height: number; width: number }> =>
|
|
18
|
+
new Promise((resolve) => {
|
|
19
|
+
Image.getSize(photo.path, (width, height) => {
|
|
20
|
+
resolve({ height, width });
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
const { height, width } = await getSize();
|
|
26
|
+
size.height = height;
|
|
27
|
+
size.width = width;
|
|
28
|
+
} catch (e) {
|
|
29
|
+
// do nothing
|
|
30
|
+
console.warn('Error get image size of picture caputred from camera ', e);
|
|
31
|
+
}
|
|
32
|
+
} else {
|
|
33
|
+
size = {
|
|
34
|
+
height: photo.height,
|
|
35
|
+
width: photo.width,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
cancelled: false,
|
|
41
|
+
source: 'camera',
|
|
42
|
+
uri: photo.path,
|
|
43
|
+
...size,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
return { cancelled: true };
|
|
47
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
let ReactNativeHapticFeedback;
|
|
2
|
+
|
|
3
|
+
try {
|
|
4
|
+
ReactNativeHapticFeedback = require('react-native-haptic-feedback');
|
|
5
|
+
} catch (e) {
|
|
6
|
+
console.warn('react-native-haptic-feedback is not installed.');
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Since react-native-haptic-feedback isn't installed by default, we've
|
|
11
|
+
* copied the types from the package here.
|
|
12
|
+
*
|
|
13
|
+
* @see https://github.com/junina-de/react-native-haptic-feedback/blob/master/index.d.ts
|
|
14
|
+
* */
|
|
15
|
+
export type HapticFeedbackTypes =
|
|
16
|
+
| 'selection'
|
|
17
|
+
| 'impactLight'
|
|
18
|
+
| 'impactMedium'
|
|
19
|
+
| 'impactHeavy'
|
|
20
|
+
| 'rigid'
|
|
21
|
+
| 'soft'
|
|
22
|
+
| 'notificationSuccess'
|
|
23
|
+
| 'notificationWarning'
|
|
24
|
+
| 'notificationError'
|
|
25
|
+
| 'clockTick'
|
|
26
|
+
| 'contextClick'
|
|
27
|
+
| 'keyboardPress'
|
|
28
|
+
| 'keyboardRelease'
|
|
29
|
+
| 'keyboardTap'
|
|
30
|
+
| 'longPress'
|
|
31
|
+
| 'textHandleMove'
|
|
32
|
+
| 'virtualKey'
|
|
33
|
+
| 'virtualKeyRelease'
|
|
34
|
+
| 'effectClick'
|
|
35
|
+
| 'effectDoubleClick'
|
|
36
|
+
| 'effectHeavyClick'
|
|
37
|
+
| 'effectTick';
|
|
38
|
+
|
|
39
|
+
export const triggerHaptic = ReactNativeHapticFeedback
|
|
40
|
+
? (method: HapticFeedbackTypes) => {
|
|
41
|
+
ReactNativeHapticFeedback.trigger(method, {
|
|
42
|
+
enableVibrateFallback: false,
|
|
43
|
+
ignoreAndroidSystemSettings: false,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
: () => {};
|
package/src/index.js
CHANGED
|
@@ -1,300 +1,38 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { Image, PermissionsAndroid, Platform } from 'react-native';
|
|
1
|
+
import { Platform } from 'react-native';
|
|
3
2
|
|
|
4
|
-
import DocumentPicker from 'react-native-document-picker';
|
|
5
|
-
import RNFS from 'react-native-fs';
|
|
6
|
-
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
|
|
7
|
-
import ImagePicker from 'react-native-image-crop-picker';
|
|
8
|
-
import ImageResizer from 'react-native-image-resizer';
|
|
9
|
-
import RNShare from 'react-native-share';
|
|
10
|
-
|
|
11
|
-
import { CameraRoll } from '@react-native-camera-roll/camera-roll';
|
|
12
|
-
import NetInfo from '@react-native-community/netinfo';
|
|
13
3
|
import { FlatList } from '@stream-io/flat-list-mvcp';
|
|
14
4
|
import { registerNativeHandlers } from 'stream-chat-react-native-core';
|
|
15
5
|
|
|
16
|
-
import
|
|
6
|
+
import {
|
|
7
|
+
compressImage,
|
|
8
|
+
deleteFile,
|
|
9
|
+
NetInfo,
|
|
10
|
+
getLocalAssetUri,
|
|
11
|
+
getPhotos,
|
|
12
|
+
pickDocument,
|
|
13
|
+
saveFile,
|
|
14
|
+
shareImage,
|
|
15
|
+
Sound,
|
|
16
|
+
takePhoto,
|
|
17
|
+
triggerHaptic,
|
|
18
|
+
Video,
|
|
19
|
+
} from './handlers';
|
|
17
20
|
|
|
18
21
|
registerNativeHandlers({
|
|
19
|
-
compressImage
|
|
20
|
-
|
|
21
|
-
const { uri: compressedUri } = await ImageResizer.createResizedImage(
|
|
22
|
-
uri,
|
|
23
|
-
height,
|
|
24
|
-
width,
|
|
25
|
-
'JPEG',
|
|
26
|
-
Math.min(Math.max(0, compressImageQuality), 1) * 100,
|
|
27
|
-
0,
|
|
28
|
-
undefined,
|
|
29
|
-
false,
|
|
30
|
-
'cover',
|
|
31
|
-
);
|
|
32
|
-
return compressedUri;
|
|
33
|
-
} catch (error) {
|
|
34
|
-
console.log(error);
|
|
35
|
-
return uri;
|
|
36
|
-
}
|
|
37
|
-
},
|
|
38
|
-
deleteFile: async ({ uri }) => {
|
|
39
|
-
try {
|
|
40
|
-
await RNFS.unlink(uri);
|
|
41
|
-
return true;
|
|
42
|
-
} catch (error) {
|
|
43
|
-
console.log('File deletion failed...');
|
|
44
|
-
return false;
|
|
45
|
-
}
|
|
46
|
-
},
|
|
22
|
+
compressImage,
|
|
23
|
+
deleteFile,
|
|
47
24
|
FlatList,
|
|
48
|
-
getLocalAssetUri
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
throw new Error('getLocalAssetUri Error');
|
|
54
|
-
}
|
|
55
|
-
},
|
|
56
|
-
getPhotos: async ({ after, first }) => {
|
|
57
|
-
try {
|
|
58
|
-
if (Platform.OS === 'android') {
|
|
59
|
-
const readExternalStoragePermissionAndroid =
|
|
60
|
-
PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE;
|
|
61
|
-
const hasPermission = await PermissionsAndroid.check(readExternalStoragePermissionAndroid);
|
|
62
|
-
if (!hasPermission) {
|
|
63
|
-
const granted = await PermissionsAndroid.request(readExternalStoragePermissionAndroid, {
|
|
64
|
-
buttonNegative: 'Deny',
|
|
65
|
-
buttonNeutral: 'Ask Me Later',
|
|
66
|
-
buttonPositive: 'Allow',
|
|
67
|
-
message: 'Permissions are required to access and share photos.',
|
|
68
|
-
title: 'Photos Access',
|
|
69
|
-
});
|
|
70
|
-
if (granted !== PermissionsAndroid.RESULTS.GRANTED) {
|
|
71
|
-
throw new Error('getPhotos Error');
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
const results = await CameraRoll.getPhotos({
|
|
76
|
-
after,
|
|
77
|
-
assetType: 'All',
|
|
78
|
-
first,
|
|
79
|
-
include: ['fileSize', 'filename', 'imageSize', 'playableDuration'],
|
|
80
|
-
});
|
|
81
|
-
const assets = results.edges.map((edge) => ({
|
|
82
|
-
...edge.node.image,
|
|
83
|
-
source: 'picker',
|
|
84
|
-
}));
|
|
85
|
-
const hasNextPage = results.page_info.has_next_page;
|
|
86
|
-
const endCursor = results.page_info.end_cursor;
|
|
87
|
-
return { assets, endCursor, hasNextPage };
|
|
88
|
-
} catch (_error) {
|
|
89
|
-
throw new Error('getPhotos Error');
|
|
90
|
-
}
|
|
91
|
-
},
|
|
92
|
-
NetInfo: {
|
|
93
|
-
addEventListener(listener) {
|
|
94
|
-
let unsubscribe;
|
|
95
|
-
// For NetInfo >= 3.x.x
|
|
96
|
-
if (NetInfo.fetch && typeof NetInfo.fetch === 'function') {
|
|
97
|
-
unsubscribe = NetInfo.addEventListener(({ isConnected, isInternetReachable }) => {
|
|
98
|
-
// Initialize with truthy value when internetReachable is still loading
|
|
99
|
-
// if it resolves to false, listener is triggered with false value and network
|
|
100
|
-
// status is updated
|
|
101
|
-
listener(isInternetReachable === null ? isConnected : isConnected && isInternetReachable);
|
|
102
|
-
});
|
|
103
|
-
return unsubscribe;
|
|
104
|
-
} else {
|
|
105
|
-
// For NetInfo < 3.x.x
|
|
106
|
-
unsubscribe = NetInfo.addEventListener('connectionChange', () => {
|
|
107
|
-
NetInfo.isConnected.fetch().then((isConnected) => {
|
|
108
|
-
listener(isConnected);
|
|
109
|
-
});
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
return unsubscribe.remove;
|
|
113
|
-
}
|
|
114
|
-
},
|
|
115
|
-
|
|
116
|
-
fetch() {
|
|
117
|
-
return new Promise((resolve, reject) => {
|
|
118
|
-
// For NetInfo >= 3.x.x
|
|
119
|
-
if (NetInfo.fetch && typeof NetInfo.fetch === 'function') {
|
|
120
|
-
NetInfo.fetch().then(({ isConnected }) => {
|
|
121
|
-
resolve(isConnected);
|
|
122
|
-
}, reject);
|
|
123
|
-
} else {
|
|
124
|
-
// For NetInfo < 3.x.x
|
|
125
|
-
NetInfo.isConnected.fetch().then((isConnected) => {
|
|
126
|
-
resolve(isConnected);
|
|
127
|
-
}, reject);
|
|
128
|
-
}
|
|
129
|
-
});
|
|
130
|
-
},
|
|
131
|
-
},
|
|
132
|
-
pickDocument: async ({ maxNumberOfFiles }) => {
|
|
133
|
-
try {
|
|
134
|
-
let res = await DocumentPicker.pickMultiple({
|
|
135
|
-
type: [DocumentPicker.types.allFiles],
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
if (maxNumberOfFiles && res.length > maxNumberOfFiles) {
|
|
139
|
-
res = res.slice(0, maxNumberOfFiles);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
return {
|
|
143
|
-
cancelled: false,
|
|
144
|
-
docs: res.map(({ name, size, type, uri }) => ({
|
|
145
|
-
name,
|
|
146
|
-
size,
|
|
147
|
-
type,
|
|
148
|
-
uri,
|
|
149
|
-
})),
|
|
150
|
-
};
|
|
151
|
-
} catch (err) {
|
|
152
|
-
return {
|
|
153
|
-
cancelled: true,
|
|
154
|
-
};
|
|
155
|
-
}
|
|
156
|
-
},
|
|
157
|
-
saveFile: async ({ fileName, fromUrl }) => {
|
|
158
|
-
try {
|
|
159
|
-
const path = RNFS.CachesDirectoryPath + '/' + encodeURIComponent(fileName);
|
|
160
|
-
await RNFS.downloadFile({ fromUrl, toFile: path }).promise;
|
|
161
|
-
return 'file://' + path;
|
|
162
|
-
} catch (error) {
|
|
163
|
-
throw new Error('Downloading image failed...');
|
|
164
|
-
}
|
|
165
|
-
},
|
|
25
|
+
getLocalAssetUri,
|
|
26
|
+
getPhotos,
|
|
27
|
+
NetInfo,
|
|
28
|
+
pickDocument,
|
|
29
|
+
saveFile,
|
|
166
30
|
SDK: 'stream-chat-react-native',
|
|
167
|
-
shareImage
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
activityItemSources:
|
|
173
|
-
Platform.OS === 'ios'
|
|
174
|
-
? [
|
|
175
|
-
{
|
|
176
|
-
item: {
|
|
177
|
-
default: {
|
|
178
|
-
content: url,
|
|
179
|
-
type: 'url',
|
|
180
|
-
},
|
|
181
|
-
},
|
|
182
|
-
linkMetadata: {
|
|
183
|
-
icon: url,
|
|
184
|
-
},
|
|
185
|
-
placeholderItem: {
|
|
186
|
-
content: url,
|
|
187
|
-
type: 'url',
|
|
188
|
-
},
|
|
189
|
-
},
|
|
190
|
-
]
|
|
191
|
-
: undefined,
|
|
192
|
-
excludedActivityTypes: [],
|
|
193
|
-
failOnCancel: false,
|
|
194
|
-
type,
|
|
195
|
-
url: Platform.OS === 'android' ? base64Url : undefined,
|
|
196
|
-
});
|
|
197
|
-
return true;
|
|
198
|
-
} catch (error) {
|
|
199
|
-
throw new Error('Sharing failed...');
|
|
200
|
-
}
|
|
201
|
-
},
|
|
202
|
-
Sound: {
|
|
203
|
-
initializeSound: null,
|
|
204
|
-
// eslint-disable-next-line react/display-name
|
|
205
|
-
Player: AudioVideoPlayer
|
|
206
|
-
? ({ onBuffer, onEnd, onLoad, onProgress, paused, soundRef, style, uri }) => (
|
|
207
|
-
<AudioVideoPlayer
|
|
208
|
-
audioOnly={true}
|
|
209
|
-
onBuffer={onBuffer}
|
|
210
|
-
onEnd={onEnd}
|
|
211
|
-
onError={(error) => {
|
|
212
|
-
console.log(error);
|
|
213
|
-
}}
|
|
214
|
-
onLoad={onLoad}
|
|
215
|
-
onProgress={onProgress}
|
|
216
|
-
paused={paused}
|
|
217
|
-
ref={soundRef}
|
|
218
|
-
source={{
|
|
219
|
-
uri,
|
|
220
|
-
}}
|
|
221
|
-
style={style}
|
|
222
|
-
/>
|
|
223
|
-
)
|
|
224
|
-
: null,
|
|
225
|
-
},
|
|
226
|
-
takePhoto: async ({ compressImageQuality = Platform.OS === 'ios' ? 0.8 : 1 }) => {
|
|
227
|
-
const photo = await ImagePicker.openCamera({
|
|
228
|
-
compressImageQuality: Math.min(Math.max(0, compressImageQuality), 1),
|
|
229
|
-
});
|
|
230
|
-
|
|
231
|
-
if (photo.height && photo.width && photo.path) {
|
|
232
|
-
let size = {};
|
|
233
|
-
if (Platform.OS === 'android') {
|
|
234
|
-
// Height and width returned by ImagePicker are incorrect on Android.
|
|
235
|
-
// The issue is described in following github issue:
|
|
236
|
-
// https://github.com/ivpusic/react-native-image-crop-picker/issues/901
|
|
237
|
-
// This we can't rely on them as it is, and we need to use Image.getSize
|
|
238
|
-
// to get accurate size.
|
|
239
|
-
const getSize = () =>
|
|
240
|
-
new Promise((resolve) => {
|
|
241
|
-
Image.getSize(photo.path, (width, height) => {
|
|
242
|
-
resolve({ height, width });
|
|
243
|
-
});
|
|
244
|
-
});
|
|
245
|
-
|
|
246
|
-
try {
|
|
247
|
-
const { height, width } = await getSize();
|
|
248
|
-
size.height = height;
|
|
249
|
-
size.width = width;
|
|
250
|
-
} catch (e) {
|
|
251
|
-
// do nothing
|
|
252
|
-
console.warning('Error get image size of picture caputred from camera ', e);
|
|
253
|
-
}
|
|
254
|
-
} else {
|
|
255
|
-
size = {
|
|
256
|
-
height: photo.height,
|
|
257
|
-
width: photo.width,
|
|
258
|
-
};
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
return {
|
|
262
|
-
cancelled: false,
|
|
263
|
-
source: 'camera',
|
|
264
|
-
uri: photo.path,
|
|
265
|
-
...size,
|
|
266
|
-
};
|
|
267
|
-
}
|
|
268
|
-
return { cancelled: true };
|
|
269
|
-
},
|
|
270
|
-
triggerHaptic: (method) => {
|
|
271
|
-
ReactNativeHapticFeedback.trigger(method, {
|
|
272
|
-
enableVibrateFallback: false,
|
|
273
|
-
ignoreAndroidSystemSettings: false,
|
|
274
|
-
});
|
|
275
|
-
},
|
|
276
|
-
// eslint-disable-next-line react/display-name
|
|
277
|
-
Video: AudioVideoPlayer
|
|
278
|
-
? ({ onBuffer, onEnd, onLoad, onProgress, paused, repeat, style, uri, videoRef }) => (
|
|
279
|
-
<AudioVideoPlayer
|
|
280
|
-
ignoreSilentSwitch={'ignore'}
|
|
281
|
-
onBuffer={onBuffer}
|
|
282
|
-
onEnd={onEnd}
|
|
283
|
-
onError={(error) => {
|
|
284
|
-
console.error(error);
|
|
285
|
-
}}
|
|
286
|
-
onLoad={onLoad}
|
|
287
|
-
onProgress={onProgress}
|
|
288
|
-
paused={paused}
|
|
289
|
-
ref={videoRef}
|
|
290
|
-
repeat={repeat}
|
|
291
|
-
source={{
|
|
292
|
-
uri,
|
|
293
|
-
}}
|
|
294
|
-
style={style}
|
|
295
|
-
/>
|
|
296
|
-
)
|
|
297
|
-
: null,
|
|
31
|
+
shareImage,
|
|
32
|
+
Sound,
|
|
33
|
+
takePhoto,
|
|
34
|
+
triggerHaptic,
|
|
35
|
+
Video,
|
|
298
36
|
});
|
|
299
37
|
|
|
300
38
|
if (Platform.OS === 'android') {
|
|
@@ -1,4 +1,23 @@
|
|
|
1
|
-
|
|
1
|
+
import { StyleProp, ViewStyle } from 'react-native';
|
|
2
|
+
|
|
3
|
+
let AudioVideoComponent:
|
|
4
|
+
| React.ComponentType<{
|
|
5
|
+
audioOnly?: boolean;
|
|
6
|
+
ignoreSilentSwitch?: 'ignore' | 'obey';
|
|
7
|
+
repeat?: boolean;
|
|
8
|
+
onBuffer: () => void;
|
|
9
|
+
onEnd: () => void;
|
|
10
|
+
onError: (error: Error) => void;
|
|
11
|
+
onLoad: () => void;
|
|
12
|
+
onProgress: () => void;
|
|
13
|
+
paused: boolean;
|
|
14
|
+
ref: React.RefObject<any>;
|
|
15
|
+
source: {
|
|
16
|
+
uri: string;
|
|
17
|
+
};
|
|
18
|
+
style: StyleProp<ViewStyle>;
|
|
19
|
+
}>
|
|
20
|
+
| undefined;
|
|
2
21
|
try {
|
|
3
22
|
// eslint-disable-next-line no-undef
|
|
4
23
|
const videoPackage = require('react-native-video');
|