@around25/react-native-gallery-media-picker
Version:
A react native component that will get all the media files (images and video) from device gallery and allow to select items
304 lines (264 loc) • 7.3 kB
JavaScript
import React, { Component } from 'react';
import { FlatList, ActivityIndicator, View, TouchableOpacity, Text, Image } from 'react-native';
import _ from 'lodash';
import moment from 'moment';
import MediaItem from '../MediaItem';
import styles from './styles';
import { existsInArray, placeInTime } from '../../utils';
const arrow = require('../../assets/images/next-arrow.png');
class MediaList extends Component {
constructor (props) {
super(props);
this.state = {
finishedLoading: false,
rows: [],
rowsSorted: {},
selected: [],
};
}
componentDidMount () {
this.setState({
rows: this.splitIntoRows(this.props.images, this.props.itemsPerRow)
});
}
/**
* @description Split all the images by their time tag (placeInTime) and create the arrays for each row with itemsPerRow elements
* @param images
* @param itemsPerRow
* @return {Array}
*/
splitIntoRows (images, itemsPerRow) {
let result = {};
let temp = {};
for ( let i = 0; i < images.length; ++ i ) {
const timestampOfImage = images[i].timestamp * 1000;
const placeInTimeOfImage = placeInTime(timestampOfImage);
if (temp[placeInTimeOfImage] === undefined) {
temp[placeInTimeOfImage] = [];
}
temp[placeInTimeOfImage].push(images[i]);
if (temp[placeInTimeOfImage].length > 0 && temp[placeInTimeOfImage].length % itemsPerRow === 0) {
if (result[placeInTimeOfImage] === undefined) {
result[placeInTimeOfImage] = [];
}
result[placeInTimeOfImage].push(temp[placeInTimeOfImage]);
temp[placeInTimeOfImage] = [];
}
}
for (let prop in temp) {
if (temp[prop].length > 0) {
while (temp[prop].length !== itemsPerRow) {
temp[prop].push(null);
}
if (result[prop] === undefined) {
result[prop] = [];
}
result[prop].push(temp[prop]);
}
}
let final = [];
let order = ['today', 'week', 'month'];
let allTimeTags = Object.keys(result).map(prop => {
if (Number.isInteger(parseInt(prop))) {
return parseInt(prop);
}
return prop;
});
let allMonths = allTimeTags.filter(prop => Number.isInteger(prop) && prop < 12);
allMonths = _.reverse(_.sortBy(allMonths));
let allYears = allTimeTags.filter(prop => Number.isInteger(prop) && !allMonths.includes(prop));
allYears = _.reverse(_.sortBy(allYears));
order = _.concat(order, allMonths, allYears);
for (let prop of order) {
if (result[prop]) {
final = _.concat(final, result[prop]);
}
}
this.setState({
rowsSorted: result
});
return final;
}
onEndReached () {
if (!this.state.finishedLoading) {
this.setState({
finishedLoading: true
});
}
}
/**
* @description Select media file function
* @param item
*/
selectMediaFile(item) {
let { maximumSelectedFiles, itemsPerRow, callback, selectSingleItem } = this.props;
let selected = this.state.selected,
index = existsInArray( selected, 'image', 'uri', item.image.uri );
if ( index >= 0 ) {
selected.splice( index, 1 );
} else {
if ( selectSingleItem ) {
selected.splice( 0, selected.length );
}
if ( selected.length < maximumSelectedFiles ) {
selected.push( item );
}
}
this.setState( {
selected: selected,
} );
callback(selected, item);
}
backToAlbums () {
this.setState({
selected: []
});
this.props.callback([], null);
this.props.onBackPress();
}
/**
* @description Render media item
* @param item
* @return {XML}
*/
renderMediaItem( item, index ) {
let {
selected,
imageMargin,
customSelectMarker,
markIcon,
itemsPerRow,
containerWidth
} = this.props;
let uri = item.image.uri;
let isSelected = (existsInArray(selected, 'image', 'uri', uri) >= 0);
return (
<MediaItem
key={uri + index}
markIcon={markIcon}
item={item}
selected={isSelected}
imageMargin={imageMargin}
customSelectMarker={customSelectMarker}
itemsPerRow={itemsPerRow}
containerWidth={containerWidth}
onClick={this.selectMediaFile.bind(this)}
/>
);
}
renderRowHeader (rowData) {
let headerTitle = placeInTime(rowData[0].timestamp * 1000);
if (this.state.rowsSorted[headerTitle].indexOf(rowData) > 0) {
return null;
}
if (Number.isInteger(headerTitle) && headerTitle < 12) {
headerTitle = moment(headerTitle).format('MMMM');
}
if (headerTitle === 'today') {
headerTitle = 'Today';
}
if (headerTitle === 'week') {
headerTitle = 'This Week';
}
if (headerTitle === 'month') {
headerTitle = 'This Month'
}
return (
<View
style={{
alignItems: 'center'
}}>
<Text
style={{
textAlign: 'center',
marginVertical: 7,
color: '#aaaaaa'
}}>{headerTitle}</Text>
</View>
);
}
/**
* @description Render list row
* @param rowData
* @return {XML}
*/
renderRow (rowData, index) {
let items = rowData.map((item) => {
if (item === null) {
return null;
}
return this.renderMediaItem(item, index);
});
return (
<View style={{}}>
{this.renderRowHeader(rowData)}
<View style={styles.row}>
{items}
</View>
</View>
);
}
/**
* @description Render footer loader when more files are fetching
* @return {*}
*/
renderFooterLoader() {
if (!this.state.finishedLoading) {
return <ActivityIndicator color={this.props.activityIndicatorColor}/>;
}
return null;
}
renderBackToAlbumsButton () {
return (
<View style={{flex: 1, padding: 7}}>
<TouchableOpacity
onPress={this.backToAlbums.bind(this)}
style={{
flex: 1,
backgroundColor: '#cccccc',
borderRadius: 8,
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'row'
}}>
<Image
source={arrow}
style={{
height: 20,
width: 20,
marginRight: 5,
transform: [{rotate: '180deg'}],
}} />
<Text>Back to albums</Text>
</TouchableOpacity>
</View>
);
}
renderList () {
return (
<View style={{flex: 14}}>
<FlatList
ListFooterComponent={this.renderFooterLoader.bind(this)}
initialNumToRender={this.props.batchSize}
onEndReached={this.onEndReached.bind(this)}
renderItem={({item, index}) => this.renderRow(item, index)}
keyExtractor={(item, index) => item[0].image.uri + item[0].timestamp + index}
data={this.state.rows}
extraData={this.props.selected}
/>
</View>
);
}
render () {
return (
<View
style={{
flex: 1
}}>
{this.renderBackToAlbumsButton()}
{this.renderList()}
</View>
);
}
}
export default MediaList;