Skip to content

Commit a1090cb

Browse files
authored
Merge pull request #2815 from GetStream/fix/asset-library-issues
fix: asset library issues
2 parents f310076 + 45488c1 commit a1090cb

File tree

11 files changed

+122
-63
lines changed

11 files changed

+122
-63
lines changed

examples/ExpoMessaging/app.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,17 @@
4747
{
4848
"microphonePermission": "$(PRODUCT_NAME) would like to use your microphone for voice recording."
4949
}
50+
],
51+
[
52+
"expo-build-properties",
53+
{
54+
"android": {
55+
"newArchEnabled": true
56+
},
57+
"ios": {
58+
"newArchEnabled": true
59+
}
60+
}
5061
]
5162
]
5263
}

examples/SampleApp/ios/Podfile.lock

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1439,6 +1439,27 @@ PODS:
14391439
- ReactCommon/turbomodule/bridging
14401440
- ReactCommon/turbomodule/core
14411441
- Yoga
1442+
- react-native-cameraroll (7.8.4):
1443+
- DoubleConversion
1444+
- glog
1445+
- hermes-engine
1446+
- RCT-Folly (= 2024.01.01.00)
1447+
- RCTRequired
1448+
- RCTTypeSafety
1449+
- React-Core
1450+
- React-debug
1451+
- React-Fabric
1452+
- React-featureflags
1453+
- React-graphics
1454+
- React-ImageManager
1455+
- React-NativeModulesApple
1456+
- React-RCTFabric
1457+
- React-rendererdebug
1458+
- React-utils
1459+
- ReactCodegen
1460+
- ReactCommon/turbomodule/bridging
1461+
- ReactCommon/turbomodule/core
1462+
- Yoga
14421463
- react-native-document-picker (9.3.1):
14431464
- DoubleConversion
14441465
- glog
@@ -2233,6 +2254,7 @@ DEPENDENCIES:
22332254
- React-Mapbuffer (from `../node_modules/react-native/ReactCommon`)
22342255
- React-microtasksnativemodule (from `../node_modules/react-native/ReactCommon/react/nativemodule/microtasks`)
22352256
- react-native-blob-util (from `../node_modules/react-native-blob-util`)
2257+
- "react-native-cameraroll (from `../node_modules/@react-native-camera-roll/camera-roll`)"
22362258
- react-native-document-picker (from `../node_modules/react-native-document-picker`)
22372259
- react-native-image-picker (from `../node_modules/react-native-image-picker`)
22382260
- "react-native-netinfo (from `../node_modules/@react-native-community/netinfo`)"
@@ -2381,6 +2403,8 @@ EXTERNAL SOURCES:
23812403
:path: "../node_modules/react-native/ReactCommon/react/nativemodule/microtasks"
23822404
react-native-blob-util:
23832405
:path: "../node_modules/react-native-blob-util"
2406+
react-native-cameraroll:
2407+
:path: "../node_modules/@react-native-camera-roll/camera-roll"
23842408
react-native-document-picker:
23852409
:path: "../node_modules/react-native-document-picker"
23862410
react-native-image-picker:
@@ -2531,6 +2555,7 @@ SPEC CHECKSUMS:
25312555
React-Mapbuffer: 858e7be7b51da78fd6a12b2461d92ea44e88c0f2
25322556
React-microtasksnativemodule: a221789bbd16b09f083a60a4bec8623e2dfbb59f
25332557
react-native-blob-util: 356047c561b3506396852bc0d7988243f74dd77d
2558+
react-native-cameraroll: 55de4896c31042eedcb0629a58ef5ff0d5a2c428
25342559
react-native-document-picker: 1e082836a633ca9e7c75758036ff42535baaf36b
25352560
react-native-image-picker: df6597d4b1878a443796be11eb2b7286ed10ece6
25362561
react-native-netinfo: 076df4f9b07f6670acf4ce9a75aac8d34c2e2ccc

examples/SampleApp/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
"@notifee/react-native": "^9.1.2",
2626
"@op-engineering/op-sqlite": "^9.3.0",
2727
"@react-native-async-storage/async-storage": "^1.21.0",
28+
"@react-native-camera-roll/camera-roll": "^7.8.4",
2829
"@react-native-community/netinfo": "^11.3.2",
2930
"@react-native-firebase/app": "^19.3.0",
3031
"@react-native-firebase/messaging": "^19.3.0",
@@ -40,7 +41,6 @@
4041
"react-native-fast-image": "^8.6.3",
4142
"react-native-gesture-handler": "^2.18.1",
4243
"react-native-haptic-feedback": "^2.3.3",
43-
"react-native-image-picker": "^7.1.2",
4444
"react-native-reanimated": "^3.16.0",
4545
"react-native-safe-area-context": "^4.14.0",
4646
"react-native-screens": "^3.34.0",

examples/SampleApp/yarn.lock

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1882,6 +1882,11 @@
18821882
dependencies:
18831883
merge-options "^3.0.4"
18841884

1885+
"@react-native-camera-roll/camera-roll@^7.8.4":
1886+
version "7.8.4"
1887+
resolved "https://registry.yarnpkg.com/@react-native-camera-roll/camera-roll/-/camera-roll-7.8.4.tgz#2422ad5ca4e55fa2973e8c03255e3dc7b330925c"
1888+
integrity sha512-CraoJLSOpiPnPsuzbJoydKrtCJY6rPAqHn7fzN/4uCOqoah4IlauMcNVSvr4N9qbemIztPrHBnDgLJtYDlgo+A==
1889+
18851890
"@react-native-community/[email protected]":
18861891
version "15.0.0"
18871892
resolved "https://registry.yarnpkg.com/@react-native-community/cli-clean/-/cli-clean-15.0.0.tgz#10c7cfde8379aaa7a60eaf4bd3920542d4af1b81"
@@ -6688,11 +6693,6 @@ react-native-haptic-feedback@^2.3.3:
66886693
resolved "https://registry.yarnpkg.com/react-native-haptic-feedback/-/react-native-haptic-feedback-2.3.3.tgz#88b6876e91399a69bd1b551fe1681b2f3dc1214e"
66896694
integrity sha512-svS4D5PxfNv8o68m9ahWfwje5NqukM3qLS48+WTdhbDkNUkOhP9rDfDSRHzlhk4zq+ISjyw95EhLeh8NkKX5vQ==
66906695

6691-
react-native-image-picker@^7.1.2:
6692-
version "7.1.2"
6693-
resolved "https://registry.yarnpkg.com/react-native-image-picker/-/react-native-image-picker-7.1.2.tgz#383849d1953caf4578874a1f5e5dd11c737bd5cd"
6694-
integrity sha512-b5y5nP60RIPxlAXlptn2QwlIuZWCUDWa/YPUVjgHc0Ih60mRiOg1PSzf0IjHSLeOZShCpirpvSPGnDExIpTRUg==
6695-
66966696
react-native-lightbox@^0.7.0:
66976697
version "0.7.0"
66986698
resolved "https://registry.yarnpkg.com/react-native-lightbox/-/react-native-lightbox-0.7.0.tgz#e52b4d7fcc141f59d7b23f0180de535e35b20ec9"

package/expo-package/src/optionalDependencies/getLocalAssetUri.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { Platform } from 'react-native';
2+
13
let MediaLibrary;
24

35
try {
@@ -12,10 +14,14 @@ if (!MediaLibrary) {
1214
);
1315
}
1416

17+
// TODO: The API is different for Expo and RN CLI. We should unify it.
1518
export const getLocalAssetUri = async (assetId: string): Promise<string | undefined> => {
1619
try {
17-
const { localUri } = await MediaLibrary.getAssetInfoAsync(assetId);
18-
return localUri;
20+
if (Platform.OS === 'ios') {
21+
const { localUri } = await MediaLibrary.getAssetInfoAsync(assetId);
22+
return localUri;
23+
}
24+
return null;
1925
} catch {
2026
throw new Error('getLocalAssetUri Error');
2127
}

package/expo-package/src/optionalDependencies/getPhotos.ts

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { Platform } from 'react-native';
2+
13
let MediaLibrary;
24

35
try {
@@ -13,6 +15,8 @@ if (!MediaLibrary) {
1315
}
1416
import type { Asset } from 'stream-chat-react-native-core';
1517

18+
import { getLocalAssetUri } from './getLocalAssetUri';
19+
1620
type ReturnType = {
1721
assets: Array<Omit<Asset, 'source'> & { source: 'picker' }>;
1822
endCursor: string | undefined;
@@ -40,16 +44,22 @@ export const getPhotos = MediaLibrary
4044
mediaType: [MediaLibrary.MediaType.photo, MediaLibrary.MediaType.video],
4145
sortBy: [MediaLibrary.SortBy.modificationTime],
4246
});
43-
const assets = results.assets.map((asset) => ({
44-
duration: asset.duration * 1000,
45-
height: asset.height,
46-
id: asset.id,
47-
name: asset.filename,
48-
source: 'picker' as const,
49-
type: asset.mediaType,
50-
uri: asset.uri,
51-
width: asset.width,
52-
}));
47+
const assets = await Promise.all(
48+
results.assets.map(async (asset) => {
49+
const localUri = await getLocalAssetUri(asset.id);
50+
return {
51+
duration: asset.duration * 1000,
52+
height: asset.height,
53+
id: asset.id,
54+
name: asset.filename,
55+
originalUri: asset.uri,
56+
source: 'picker' as const,
57+
type: asset.mediaType,
58+
uri: localUri || asset.uri,
59+
width: asset.width,
60+
};
61+
}),
62+
);
5363

5464
const hasNextPage = results.hasNextPage;
5565
const endCursor = results.endCursor;

package/native-package/src/optionalDependencies/getLocalAssetUri.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { Platform } from 'react-native';
2+
13
let CameraRollDependency;
24

35
try {
@@ -12,8 +14,11 @@ try {
1214
export const getLocalAssetUri = CameraRollDependency
1315
? async (remoteUri: string) => {
1416
try {
15-
const localUri = await CameraRollDependency.CameraRoll.save(remoteUri);
16-
return localUri;
17+
if (Platform.OS === 'ios') {
18+
const imageData = await CameraRollDependency.CameraRoll.iosGetImageDataById(remoteUri);
19+
return imageData?.node?.image?.filepath;
20+
}
21+
return remoteUri;
1722
} catch {
1823
throw new Error('getLocalAssetUri Error');
1924
}

package/native-package/src/optionalDependencies/getPhotos.ts

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ try {
1313

1414
import type { Asset } from 'stream-chat-react-native-core';
1515

16+
import { getLocalAssetUri } from './getLocalAssetUri';
17+
1618
type ReturnType = {
1719
assets: Array<Omit<Asset, 'source'> & { source: 'picker' }>;
1820
endCursor: string | undefined;
@@ -82,15 +84,23 @@ export const getPhotos = CameraRollDependency
8284
first,
8385
include: ['fileSize', 'filename', 'imageSize', 'playableDuration'],
8486
});
85-
const assets = results.edges.map((edge) => ({
86-
...edge.node.image,
87-
duration: edge.node.image.playableDuration * 1000,
88-
// since we include filename, fileSize in the query, we can safely assume it will be defined
89-
name: edge.node.image.filename as string,
90-
size: edge.node.image.fileSize as number,
91-
source: 'picker' as const,
92-
type: edge.node.type,
93-
}));
87+
const assets = await Promise.all(
88+
results.edges.map(async (edge) => {
89+
const originalUri = edge.node?.image?.uri;
90+
const uri = getLocalAssetUri ? await getLocalAssetUri(originalUri) : originalUri;
91+
return {
92+
...edge.node.image,
93+
duration: edge.node.image.playableDuration * 1000,
94+
// since we include filename, fileSize in the query, we can safely assume it will be defined
95+
name: edge.node.image.filename as string,
96+
originalUri,
97+
size: edge.node.image.fileSize as number,
98+
source: 'picker' as const,
99+
type: edge.node.type,
100+
uri,
101+
};
102+
}),
103+
);
94104
const hasNextPage = results.page_info.has_next_page;
95105
const endCursor = results.page_info.end_cursor;
96106
return { assets, endCursor, hasNextPage, iOSLimited: !!results.limited };

package/src/components/AttachmentPicker/AttachmentPicker.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -237,9 +237,13 @@ export const AttachmentPicker = React.forwardRef(
237237
// `id` is available for Expo MediaLibrary while Cameraroll doesn't share id therefore we use `uri`
238238
selected:
239239
selectedImages.some((image) =>
240-
image.id ? image.id === asset.id : image.uri === asset.uri,
240+
image.id
241+
? image.id === asset.id
242+
: image.uri === asset.uri || image.originalUri === asset.uri,
241243
) ||
242-
selectedFiles.some((file) => (file.id ? file.id === asset.id : file.uri === asset.uri)),
244+
selectedFiles.some((file) =>
245+
file.id ? file.id === asset.id : file.uri === asset.uri || file.originalUri === asset.uri,
246+
),
243247
selectedFiles,
244248
selectedImages,
245249
setSelectedFiles,

package/src/components/AttachmentPicker/components/AttachmentPickerItem.tsx

Lines changed: 18 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React from 'react';
22

3-
import { Alert, ImageBackground, Platform, StyleSheet, Text, View } from 'react-native';
3+
import { Alert, ImageBackground, StyleSheet, Text, View } from 'react-native';
44

55
import { TouchableOpacity } from '@gorhom/bottom-sheet';
66
import { lookup } from 'mime-types';
@@ -10,7 +10,6 @@ import { useTheme } from '../../../contexts/themeContext/ThemeContext';
1010
import { useTranslationContext } from '../../../contexts/translationContext/TranslationContext';
1111
import { useViewport } from '../../../hooks/useViewport';
1212
import { Recorder } from '../../../icons';
13-
import { getLocalAssetUri } from '../../../native';
1413
import type { Asset, File } from '../../../types/types';
1514
import { getDurationLabelFromDuration } from '../../../utils/utils';
1615

@@ -49,18 +48,14 @@ const AttachmentVideo = (props: AttachmentVideoProps) => {
4948
},
5049
} = useTheme();
5150

52-
const { duration: videoDuration, uri } = asset;
51+
const { duration: videoDuration, id: assetId, originalUri, uri } = asset;
5352

5453
const durationLabel = getDurationLabelFromDuration(videoDuration);
5554

5655
const size = vw(100) / (numberOfAttachmentPickerImageColumns || 3) - 2;
5756

5857
/* Patches video files with uri and mimetype */
59-
const patchVideoFile = async (files: File[]) => {
60-
// For the case of Expo CLI where you need to fetch the file uri from file id. Here it is only done for iOS since for android the file.uri is fine.
61-
const localAssetURI =
62-
Platform.OS === 'ios' && asset.id && getLocalAssetUri && (await getLocalAssetUri(asset.id));
63-
const uri = localAssetURI || asset.uri || '';
58+
const patchVideoFile = (files: File[]) => {
6459
// We need a mime-type to upload a video file.
6560
const mimeType = lookup(asset.name) || 'multipart/form-data';
6661
return [
@@ -70,26 +65,29 @@ const AttachmentVideo = (props: AttachmentVideoProps) => {
7065
id: asset.id,
7166
mimeType,
7267
name: asset.name,
68+
originalUri,
7369
size: asset.size,
7470
uri,
7571
},
7672
];
7773
};
7874

79-
const updateSelectedFiles = async () => {
75+
const updateSelectedFiles = () => {
8076
if (numberOfUploads >= maxNumberOfFiles) {
8177
Alert.alert(t('Maximum number of files reached'));
8278
return;
8379
}
84-
const files = await patchVideoFile(selectedFiles);
80+
const files = patchVideoFile(selectedFiles);
8581
setSelectedFiles(files);
8682
};
8783

8884
const onPressVideo = () => {
8985
if (selected) {
9086
setSelectedFiles((files) =>
9187
// `id` is available for Expo MediaLibrary while Cameraroll doesn't share id therefore we use `uri`
92-
files.filter((file) => (file.id ? file.id !== asset.id : file.uri !== asset.uri)),
88+
files.filter((file) =>
89+
file.id ? file.id !== assetId : file.uri !== uri && file.originalUri !== uri,
90+
),
9391
);
9492
} else {
9593
updateSelectedFiles();
@@ -99,7 +97,7 @@ const AttachmentVideo = (props: AttachmentVideoProps) => {
9997
return (
10098
<TouchableOpacity onPress={onPressVideo}>
10199
<ImageBackground
102-
source={{ uri }}
100+
source={{ uri: originalUri }}
103101
style={[
104102
{
105103
height: size,
@@ -149,43 +147,31 @@ const AttachmentImage = (props: AttachmentImageProps) => {
149147

150148
const size = vw(100) / (numberOfAttachmentPickerImageColumns || 3) - 2;
151149

152-
const { uri } = asset;
150+
const { id: assetId, originalUri, uri } = asset;
153151

154-
/* Patches image files with uri */
155-
const patchImageFile = async (images: Asset[]) => {
156-
// For the case of Expo CLI where you need to fetch the file uri from file id. Here it is only done for iOS since for android the file.uri is fine.
157-
const localAssetURI =
158-
Platform.OS === 'ios' && asset.id && getLocalAssetUri && (await getLocalAssetUri(asset.id));
159-
const uri = localAssetURI || asset.uri || '';
160-
return [
161-
...images,
162-
{
163-
...asset,
164-
uri,
165-
},
166-
];
167-
};
168-
169-
const updateSelectedImages = async () => {
152+
const updateSelectedImages = () => {
170153
if (numberOfUploads >= maxNumberOfFiles) {
171154
Alert.alert(t('Maximum number of files reached'));
172155
return;
173156
}
174-
const images = await patchImageFile(selectedImages);
175-
setSelectedImages(images);
157+
setSelectedImages([...selectedImages, asset]);
176158
};
177159

178160
const onPressImage = () => {
179161
if (selected) {
180162
// `id` is available for Expo MediaLibrary while Cameraroll doesn't share id therefore we use `uri`
181163
setSelectedImages((images) =>
182-
images.filter((image) => (image.id ? image.id !== asset.id : image.uri !== asset.uri)),
164+
images.filter((image) =>
165+
assetId ? image.id !== assetId : image.uri !== uri && originalUri !== uri,
166+
),
183167
);
184168
} else {
185169
updateSelectedImages();
186170
}
187171
};
188172

173+
console.log('URIS: IMG: ', uri, originalUri);
174+
189175
return (
190176
<TouchableOpacity onPress={onPressImage}>
191177
<ImageBackground

0 commit comments

Comments
 (0)