UNPKG

react-native-camera-roll-picker

Version:

A React Native component providing images selection from camera roll

305 lines (264 loc) 6.89 kB
import React, { Component } from 'react'; import { Platform, StyleSheet, View, Text, FlatList, ActivityIndicator, } from 'react-native'; import { CameraRoll } from "@react-native-camera-roll/camera-roll"; import PropTypes from 'prop-types'; import Row from './Row'; import ImageItem from './ImageItem'; const styles = StyleSheet.create({ wrapper: { flexGrow: 1, }, loader: { flexGrow: 1, justifyContent: 'center', alignItems: 'center', }, }); // helper functions const arrayObjectIndexOf = (array, property, value) => array.map(o => o[property]).indexOf(value); const nEveryRow = (data, n) => { const result = []; let temp = []; for (let i = 0; i < data.length; ++i) { if (i > 0 && i % n === 0) { result.push(temp); temp = []; } temp.push(data[i]); } if (temp.length > 0) { while (temp.length !== n) { temp.push(null); } result.push(temp); } return result; }; class CameraRollPicker extends Component { constructor(props) { super(props); this.state = { images: [], selected: this.props.selected, lastCursor: null, initialLoading: true, loadingMore: false, noMore: false, data: [], }; this.renderFooterSpinner = this.renderFooterSpinner.bind(this); this.onEndReached = this.onEndReached.bind(this); this.renderRow = this.renderRow.bind(this); this.selectImage = this.selectImage.bind(this); this.renderImage = this.renderImage.bind(this); } componentWillMount() { this.fetch(); } componentWillReceiveProps(nextProps) { this.setState({ selected: nextProps.selected, }); } onEndReached() { if (!this.state.noMore) { this.fetch(); } } appendImages(data) { const assets = data.edges; const newState = { loadingMore: false, initialLoading: false, }; if (!data.page_info.has_next_page) { newState.noMore = true; } if (assets.length > 0) { newState.lastCursor = data.page_info.end_cursor; newState.images = this.state.images.concat(assets); newState.data = nEveryRow(newState.images, this.props.imagesPerRow); } this.setState(newState); } fetch() { if (!this.state.loadingMore) { this.setState({ loadingMore: true }, () => { this.doFetch(); }); } } doFetch() { const { groupTypes, assetType } = this.props; const fetchParams = { first: 100, groupTypes, assetType, }; if (Platform.OS === 'android') { // not supported in android delete fetchParams.groupTypes; } if (this.state.lastCursor) { fetchParams.after = this.state.lastCursor; } CameraRoll.getPhotos(fetchParams) .then(data => this.appendImages(data), e => console.log(e)); } selectImage(image) { const { maximum, imagesPerRow, callback, selectSingleItem, } = this.props; const { selected } = this.state; const index = arrayObjectIndexOf(selected, 'uri', image.uri); if (index >= 0) { selected.splice(index, 1); } else { if (selectSingleItem) { selected.splice(0, selected.length); } if (selected.length < maximum) { selected.push(image); } } this.setState({ selected, data: nEveryRow(this.state.images, imagesPerRow), }); callback(selected, image); } renderImage(item) { const { selected } = this.state; const { imageMargin, selectedMarker, imagesPerRow, containerWidth, } = this.props; const { uri } = item.node.image; const isSelected = (arrayObjectIndexOf(selected, 'uri', uri) >= 0); return ( <ImageItem key={uri} item={item} selected={isSelected} imageMargin={imageMargin} selectedMarker={selectedMarker} imagesPerRow={imagesPerRow} containerWidth={containerWidth} onClick={this.selectImage} /> ); } renderRow(item) { // item is an array of objects const isSelected = item.map((imageItem) => { if (!imageItem) return false; const { uri } = imageItem.node.image; return arrayObjectIndexOf(this.state.selected, 'uri', uri) >= 0; }); return (<Row rowData={item} isSelected={isSelected} selectImage={this.selectImage} imagesPerRow={this.props.imagesPerRow} containerWidth={this.props.containerWidth} imageMargin={this.props.imageMargin} selectedMarker={this.props.selectedMarker} />); } renderFooterSpinner() { if (!this.state.noMore) { return <ActivityIndicator style={styles.spinner} />; } return null; } render() { const { initialNumToRender, imageMargin, backgroundColor, emptyText, emptyTextStyle, loader, } = this.props; if (this.state.initialLoading) { return ( <View style={[styles.loader, { backgroundColor }]}> { loader || <ActivityIndicator /> } </View> ); } const flatListOrEmptyText = this.state.data.length > 0 ? ( <FlatList style={{ flex: 1 }} ListFooterComponent={this.renderFooterSpinner} initialNumToRender={initialNumToRender} onEndReached={this.onEndReached} renderItem={({ item }) => this.renderRow(item)} keyExtractor={item => item[0].node.image.uri} data={this.state.data} extraData={this.state.selected} /> ) : ( <Text style={[{ textAlign: 'center' }, emptyTextStyle]}>{emptyText}</Text> ); return ( <View style={[styles.wrapper, { padding: imageMargin, paddingRight: 0, backgroundColor }]} > {flatListOrEmptyText} </View> ); } } CameraRollPicker.propTypes = { initialNumToRender: PropTypes.number, groupTypes: PropTypes.oneOf([ 'Album', 'All', 'Event', 'Faces', 'Library', 'PhotoStream', 'SavedPhotos', ]), maximum: PropTypes.number, assetType: PropTypes.oneOf([ 'Photos', 'Videos', 'All', ]), selectSingleItem: PropTypes.bool, imagesPerRow: PropTypes.number, imageMargin: PropTypes.number, containerWidth: PropTypes.number, callback: PropTypes.func, selected: PropTypes.array, selectedMarker: PropTypes.element, backgroundColor: PropTypes.string, emptyText: PropTypes.string, emptyTextStyle: Text.propTypes.style, loader: PropTypes.node, }; CameraRollPicker.defaultProps = { initialNumToRender: 5, groupTypes: 'SavedPhotos', maximum: 15, imagesPerRow: 3, imageMargin: 5, selectSingleItem: false, assetType: 'Photos', backgroundColor: 'white', selected: [], callback(selectedImages, currentImage) { console.log(currentImage); console.log(selectedImages); }, emptyText: 'No photos.', }; export default CameraRollPicker;