react-native-cameraroll-image-picker
Version:
257 lines (256 loc) • 10.7 kB
JavaScript
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { useEffect, useState } from "react";
import { Dimensions, FlatList, PermissionsAndroid, Platform, Text, View, } from "react-native";
import ImageItem from "./ImageItem";
import CameraRoll from "@react-native-community/cameraroll";
import { check, PERMISSIONS, RESULTS } from "react-native-permissions";
export const getAlbums = async () => {
const albumsData = await CameraRoll.getAlbums({
assetType: "Photos",
});
const newAlbums = [];
for (let i = 0; i < albumsData.length; i++) {
const newObj = {};
const d = albumsData[i];
newObj.label = d.title;
newObj.value = d.title;
newObj.count = d.count;
newAlbums.push(newObj);
}
return [{ label: "All", value: "All" }, ...newAlbums];
};
const { width } = Dimensions.get("screen");
export const ImagePicker = ({ ref, initialNumToRender = 50, groupTypes = "All", assetType = "Photos", maximum = 15, imagesPerRow = 3, imageMargin = 1, containerWidth = width, backgroundColor = "white", onChangePhotosEvent, onMaxSelectedEvent, getAlbumsData, onChangeAlbumEvent, album = "All", albums = [], isMultiSelect = false, isOnlySelectToday = false, photoHeaderComponent, emptyComponent, emptyText, emptyTextStyle, loader, }) => {
const PHOTO_LENGTH = initialNumToRender;
const MAX_SELECT_PHOTO_LENGTH = maximum;
const IMAGE_SIZE = containerWidth / imagesPerRow - (imageMargin - imageMargin / imagesPerRow);
const [selected, setSelected] = useState([]);
const [photos, setPhotos] = useState([]);
const [galleryInfo, setGalleryInfo] = useState({
end_cursor: "",
has_next_page: false,
});
const options = album === "All"
? {
first: PHOTO_LENGTH,
assetType,
groupTypes,
}
: {
first: PHOTO_LENGTH,
assetType,
groupName: album,
groupTypes,
};
const registRef = () => {
if (ref)
ref.current = {
getAlbum: albumHandler.get(),
...ref.current,
};
};
class Photo {
constructor() { }
/**
* @param isDuplicateBug There is a bug that pagination cannot be done on certain devices. If there is a bug, images are received in bulk.
*/
get = async (isDuplicateBug = false) => {
const date = new Date().setHours(0, 0, 0, 0);
const newPhotoData = await CameraRoll.getPhotos({
...options,
first: isDuplicateBug ? 5000 : options?.first,
fromTime: isOnlySelectToday ? date : undefined,
});
const newPhotos = this.makePhotoBudle(newPhotoData);
if (newPhotos.length === 0) {
console.warn("image length 0");
return;
}
this.set(newPhotos);
this.setGalleryInfo(newPhotoData.page_info);
};
getMore = async () => {
if (!galleryInfo.has_next_page || !galleryInfo?.end_cursor)
return;
const newPhotoData = await CameraRoll.getPhotos({
after: galleryInfo.end_cursor,
...options,
});
const newPhotos = this.makePhotoBudle(newPhotoData);
const isDuplicate = await this.bypassDuplicateImageBug(newPhotos);
if (isDuplicate)
return;
this.set([...photos, ...newPhotos]);
this.setGalleryInfo(newPhotoData.page_info);
};
/**
* Check the bug where pagination is not working on a specific device
*/
bypassDuplicateImageBug = async (newPhotos) => {
if (photos.length === 0)
return false;
const uriArr = photos.map((item) => item.uri);
const isDuplicate = uriArr.includes(newPhotos[0].uri);
if (!isDuplicate)
return false;
await this.get(true);
return true;
};
set = (photos) => {
setPhotos(photos);
};
setGalleryInfo = (pageInfo) => {
setGalleryInfo(pageInfo);
};
/**
* Returns Photo Array
* @param newPhotoData Image bundle obtained by CameraRoll's getPhotos method [Array]
* @returns Array{name, type, uri}
*/
makePhotoBudle = (newPhotoData) => {
let newPhotos = [];
for (let i = 0; i < newPhotoData.edges.length; i++) {
const edge = newPhotoData.edges[i];
const newImageObj = {
name: `image${i}.jpg`,
type: "image/jpeg",
uri: edge.node.image.uri,
location: edge.node.location,
timestamp: edge.node.timestamp,
};
newPhotos.push(newImageObj);
}
return newPhotos;
};
}
const photoHandler = new Photo();
const albumHandler = {
get: async function () {
const albumsData = await CameraRoll.getAlbums({
assetType: "Photos",
});
const newAlbums = await this.makeAlbumBudle(albumsData);
getAlbumsData && getAlbumsData([...albums, ...newAlbums]);
},
makeAlbumBudle: (albumsData) => {
const newAlbums = [];
for (let i = 0; i < albumsData.length; i++) {
const newObj = { label: "", value: "", count: 0 };
const d = albumsData[i];
newObj.label = d.title;
newObj.value = d.title;
newObj.count = d.count;
newAlbums.push(newObj);
}
return newAlbums;
},
};
const handleSelect = ({ photo, order, isChecked, }) => {
if (isMultiSelect) {
const copiedPhotos = selected.slice();
if (order === -1) {
if (selected.length === MAX_SELECT_PHOTO_LENGTH) {
onMaxSelectedEvent && onMaxSelectedEvent();
}
else {
copiedPhotos.push(photo);
}
}
else {
copiedPhotos.splice(order, 1);
}
setSelected(copiedPhotos);
onChangePhotosEvent &&
onChangePhotosEvent({
selected: copiedPhotos,
item: photo,
index: order,
isChecked: isChecked,
});
return;
}
setSelected([photo]);
onChangePhotosEvent &&
onChangePhotosEvent({
selected: [photo],
item: photo,
index: order,
isChecked: isChecked,
});
};
const checkAndroidReadStoragePermission = async () => {
const isGranted = await PermissionsAndroid.check(PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE);
if (!isGranted)
console.warn("no atuthentification 'READ_EXTERNAL_STORAGE'");
};
const checkAndroidWriteStoragePermission = async () => {
const isGranted = await PermissionsAndroid.check(PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE);
if (!isGranted)
console.warn("no atuthentification 'WRITE_EXTERNAL_STORAGE'");
};
const checkIOSLibraryPermission = async () => {
const { GRANTED } = RESULTS;
const result = await check(PERMISSIONS.IOS.PHOTO_LIBRARY);
if (result !== GRANTED)
console.warn("no atuthentification 'PHOTO_LIBRARY'");
};
const checkPhotoLibraryPermission = async () => {
if (Platform.OS === "android") {
checkAndroidReadStoragePermission();
checkAndroidWriteStoragePermission();
}
else {
checkIOSLibraryPermission();
}
};
useEffect(() => {
checkPhotoLibraryPermission();
registRef();
}, []);
useEffect(() => {
photoHandler.get();
onChangeAlbumEvent && onChangeAlbumEvent(album);
}, [album]);
const handleRenderItem = ({ item, index, }) => {
const isMarginRight = (index + 1) % imagesPerRow !== 0;
const selectedIndex = selected.findIndex((photo) => photo.uri === item.uri);
let isChecked = false;
if (selectedIndex !== -1)
isChecked = true;
const isToday = new Date(item.timestamp * 1000).toDateString() ===
new Date().toDateString();
const shouldDim = isOnlySelectToday && !isToday;
const formattedDate = new Date(item.timestamp * 1000)
.toISOString()
.slice(0, 10)
.replace(/-/g, ".");
return (_jsxs(View, { style: {
position: "relative",
width: IMAGE_SIZE,
height: IMAGE_SIZE,
marginRight: isMarginRight ? imageMargin : 0,
marginBottom: imageMargin,
}, children: [_jsx(ImageItem, { item: item, isChecked: isChecked, selectedIndex: selectedIndex, handleSelect: () => handleSelect({ photo: item, order: selectedIndex, isChecked }), isMultiSelect: isMultiSelect, styles: {
width: IMAGE_SIZE,
height: IMAGE_SIZE,
} }), shouldDim && (_jsx(View, { style: {
position: "absolute",
top: 0,
left: 0,
width: "100%",
height: "100%",
backgroundColor: "black",
opacity: 0.5,
}, children: _jsx(View, { style: {
position: "absolute",
bottom: 5,
right: 5,
backgroundColor: "rgba(0, 0, 0, 0.5)",
padding: 2,
borderRadius: 3,
}, children: _jsx(Text, { style: { color: "white", fontSize: 10 }, children: formattedDate }) }) }))] }));
};
return (_jsx(View, { style: { backgroundColor, flex: 1 }, children: _jsx(FlatList, { style: {
width: containerWidth,
}, data: photos, renderItem: handleRenderItem, ListHeaderComponent: isOnlySelectToday ? photoHeaderComponent : null, keyExtractor: (item) => item.uri, ListEmptyComponent: emptyComponent, numColumns: imagesPerRow, onEndReached: () => photoHandler.getMore(), onEndReachedThreshold: 0.8 }) }));
};