2015-03-11 02:11:28 +00:00
|
|
|
/**
|
2017-05-06 03:50:47 +00:00
|
|
|
* Copyright (c) 2015-present, Facebook, Inc.
|
2016-07-12 12:51:57 +00:00
|
|
|
*
|
2018-02-17 02:24:55 +00:00
|
|
|
* This source code is licensed under the MIT license found in the
|
|
|
|
* LICENSE file in the root directory of this source tree.
|
2016-07-12 12:51:57 +00:00
|
|
|
*
|
2018-05-11 20:32:37 +00:00
|
|
|
* @format
|
2015-03-23 17:06:16 +00:00
|
|
|
* @flow
|
2015-03-11 02:11:28 +00:00
|
|
|
*/
|
2018-05-11 20:32:37 +00:00
|
|
|
|
2015-03-11 02:11:28 +00:00
|
|
|
'use strict';
|
|
|
|
|
2016-04-09 03:36:40 +00:00
|
|
|
var React = require('react');
|
2017-07-07 21:24:25 +00:00
|
|
|
var createReactClass = require('create-react-class');
|
2017-04-12 23:09:48 +00:00
|
|
|
const PropTypes = require('prop-types');
|
2016-04-09 03:36:40 +00:00
|
|
|
var ReactNative = require('react-native');
|
2015-03-11 02:11:28 +00:00
|
|
|
var {
|
2016-06-14 05:15:47 +00:00
|
|
|
ActivityIndicator,
|
2017-03-22 22:58:29 +00:00
|
|
|
Alert,
|
2015-03-11 02:11:28 +00:00
|
|
|
CameraRoll,
|
|
|
|
Image,
|
|
|
|
ListView,
|
2017-03-22 22:58:29 +00:00
|
|
|
PermissionsAndroid,
|
2016-01-14 12:31:51 +00:00
|
|
|
Platform,
|
2015-03-11 02:11:28 +00:00
|
|
|
StyleSheet,
|
|
|
|
View,
|
2016-04-09 03:36:40 +00:00
|
|
|
} = ReactNative;
|
2015-03-11 02:11:28 +00:00
|
|
|
|
|
|
|
var groupByEveryN = require('groupByEveryN');
|
|
|
|
var logError = require('logError');
|
|
|
|
|
|
|
|
var propTypes = {
|
|
|
|
/**
|
|
|
|
* The group where the photos will be fetched from. Possible
|
|
|
|
* values are 'Album', 'All', 'Event', 'Faces', 'Library', 'PhotoStream'
|
|
|
|
* and SavedPhotos.
|
|
|
|
*/
|
2017-04-12 23:09:48 +00:00
|
|
|
groupTypes: PropTypes.oneOf([
|
2015-03-11 02:11:28 +00:00
|
|
|
'Album',
|
|
|
|
'All',
|
|
|
|
'Event',
|
|
|
|
'Faces',
|
|
|
|
'Library',
|
|
|
|
'PhotoStream',
|
|
|
|
'SavedPhotos',
|
|
|
|
]),
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Number of images that will be fetched in one page.
|
|
|
|
*/
|
2017-04-12 23:09:48 +00:00
|
|
|
batchSize: PropTypes.number,
|
2015-03-11 02:11:28 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* A function that takes a single image as a parameter and renders it.
|
|
|
|
*/
|
2017-04-12 23:09:48 +00:00
|
|
|
renderImage: PropTypes.func,
|
2015-03-11 02:11:28 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* imagesPerRow: Number of images to be shown in each row.
|
|
|
|
*/
|
2017-04-12 23:09:48 +00:00
|
|
|
imagesPerRow: PropTypes.number,
|
2015-06-01 22:46:06 +00:00
|
|
|
|
2018-05-11 20:32:37 +00:00
|
|
|
/**
|
2015-06-01 22:46:06 +00:00
|
|
|
* The asset type, one of 'Photos', 'Videos' or 'All'
|
|
|
|
*/
|
2018-05-11 20:32:37 +00:00
|
|
|
assetType: PropTypes.oneOf(['Photos', 'Videos', 'All']),
|
2015-03-11 02:11:28 +00:00
|
|
|
};
|
|
|
|
|
2017-07-07 21:24:25 +00:00
|
|
|
var CameraRollView = createReactClass({
|
|
|
|
displayName: 'CameraRollView',
|
2015-03-11 02:11:28 +00:00
|
|
|
propTypes: propTypes,
|
|
|
|
|
2015-03-23 17:06:16 +00:00
|
|
|
getDefaultProps: function(): Object {
|
2015-03-11 02:11:28 +00:00
|
|
|
return {
|
|
|
|
groupTypes: 'SavedPhotos',
|
|
|
|
batchSize: 5,
|
|
|
|
imagesPerRow: 1,
|
2015-06-01 22:46:06 +00:00
|
|
|
assetType: 'Photos',
|
2015-03-11 02:11:28 +00:00
|
|
|
renderImage: function(asset) {
|
|
|
|
var imageSize = 150;
|
|
|
|
var imageStyle = [styles.image, {width: imageSize, height: imageSize}];
|
2018-05-11 20:32:37 +00:00
|
|
|
return <Image source={asset.node.image} style={imageStyle} />;
|
2015-03-11 02:11:28 +00:00
|
|
|
},
|
|
|
|
};
|
|
|
|
},
|
|
|
|
|
|
|
|
getInitialState: function() {
|
2015-03-20 15:43:51 +00:00
|
|
|
var ds = new ListView.DataSource({rowHasChanged: this._rowHasChanged});
|
2015-03-11 02:11:28 +00:00
|
|
|
|
|
|
|
return {
|
2015-03-23 17:06:16 +00:00
|
|
|
assets: ([]: Array<Image>),
|
2015-03-11 02:11:28 +00:00
|
|
|
groupTypes: this.props.groupTypes,
|
2018-05-11 20:32:37 +00:00
|
|
|
lastCursor: (null: ?string),
|
2015-06-01 22:46:06 +00:00
|
|
|
assetType: this.props.assetType,
|
2015-03-11 02:11:28 +00:00
|
|
|
noMore: false,
|
|
|
|
loadingMore: false,
|
|
|
|
dataSource: ds,
|
|
|
|
};
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This should be called when the image renderer is changed to tell the
|
|
|
|
* component to re-render its assets.
|
|
|
|
*/
|
|
|
|
rendererChanged: function() {
|
2015-03-20 15:43:51 +00:00
|
|
|
var ds = new ListView.DataSource({rowHasChanged: this._rowHasChanged});
|
2015-03-11 02:11:28 +00:00
|
|
|
this.state.dataSource = ds.cloneWithRows(
|
2017-03-05 03:29:24 +00:00
|
|
|
// $FlowFixMe(>=0.41.0)
|
2018-05-11 20:32:37 +00:00
|
|
|
groupByEveryN(this.state.assets, this.props.imagesPerRow),
|
2015-03-11 02:11:28 +00:00
|
|
|
);
|
|
|
|
},
|
|
|
|
|
|
|
|
componentDidMount: function() {
|
|
|
|
this.fetch();
|
|
|
|
},
|
|
|
|
|
2018-03-20 01:24:11 +00:00
|
|
|
/* $FlowFixMe(>=0.68.0 site=react_native_fb) This comment suppresses an error
|
|
|
|
* found when Flow v0.68 was deployed. To see the error delete this comment
|
|
|
|
* and run Flow. */
|
2018-02-08 18:26:45 +00:00
|
|
|
UNSAFE_componentWillReceiveProps: function(nextProps: {groupTypes?: string}) {
|
2015-03-11 02:11:28 +00:00
|
|
|
if (this.props.groupTypes !== nextProps.groupTypes) {
|
|
|
|
this.fetch(true);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2017-03-22 22:58:29 +00:00
|
|
|
_fetch: async function(clear?: boolean) {
|
2015-03-11 02:11:28 +00:00
|
|
|
if (clear) {
|
|
|
|
this.setState(this.getInitialState(), this.fetch);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-03-22 22:58:29 +00:00
|
|
|
if (Platform.OS === 'android') {
|
|
|
|
const result = await PermissionsAndroid.request(
|
|
|
|
PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE,
|
|
|
|
{
|
|
|
|
title: 'Permission Explanation',
|
2017-05-06 03:50:47 +00:00
|
|
|
message: 'RNTester would like to access your pictures.',
|
2017-03-22 22:58:29 +00:00
|
|
|
},
|
|
|
|
);
|
|
|
|
if (result !== 'granted') {
|
|
|
|
Alert.alert('Access to pictures was denied.');
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const fetchParams: Object = {
|
2015-03-11 02:11:28 +00:00
|
|
|
first: this.props.batchSize,
|
2015-06-01 22:46:06 +00:00
|
|
|
groupTypes: this.props.groupTypes,
|
|
|
|
assetType: this.props.assetType,
|
2015-03-11 02:11:28 +00:00
|
|
|
};
|
2016-06-14 05:15:47 +00:00
|
|
|
if (Platform.OS === 'android') {
|
2016-01-14 12:31:51 +00:00
|
|
|
// not supported in android
|
|
|
|
delete fetchParams.groupTypes;
|
|
|
|
}
|
2015-03-11 02:11:28 +00:00
|
|
|
if (this.state.lastCursor) {
|
|
|
|
fetchParams.after = this.state.lastCursor;
|
|
|
|
}
|
|
|
|
|
2017-03-22 22:58:29 +00:00
|
|
|
try {
|
|
|
|
const data = await CameraRoll.getPhotos(fetchParams);
|
|
|
|
this._appendAssets(data);
|
|
|
|
} catch (e) {
|
|
|
|
logError(e);
|
|
|
|
}
|
2015-03-11 02:11:28 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Fetches more images from the camera roll. If clear is set to true, it will
|
|
|
|
* set the component to its initial state and re-fetch the images.
|
|
|
|
*/
|
2015-03-23 17:06:16 +00:00
|
|
|
fetch: function(clear?: boolean) {
|
2015-03-11 02:11:28 +00:00
|
|
|
if (!this.state.loadingMore) {
|
2018-05-11 20:32:37 +00:00
|
|
|
this.setState({loadingMore: true}, () => {
|
|
|
|
this._fetch(clear);
|
|
|
|
});
|
2015-03-11 02:11:28 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
render: function() {
|
|
|
|
return (
|
|
|
|
<ListView
|
|
|
|
renderRow={this._renderRow}
|
|
|
|
renderFooter={this._renderFooterSpinner}
|
|
|
|
onEndReached={this._onEndReached}
|
|
|
|
style={styles.container}
|
|
|
|
dataSource={this.state.dataSource}
|
2017-03-22 22:58:29 +00:00
|
|
|
enableEmptySections
|
2015-03-11 02:11:28 +00:00
|
|
|
/>
|
|
|
|
);
|
|
|
|
},
|
|
|
|
|
2015-03-23 17:06:16 +00:00
|
|
|
_rowHasChanged: function(r1: Array<Image>, r2: Array<Image>): boolean {
|
2015-03-11 02:11:28 +00:00
|
|
|
if (r1.length !== r2.length) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (var i = 0; i < r1.length; i++) {
|
|
|
|
if (r1[i] !== r2[i]) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
},
|
|
|
|
|
|
|
|
_renderFooterSpinner: function() {
|
|
|
|
if (!this.state.noMore) {
|
2016-07-19 06:18:03 +00:00
|
|
|
return <ActivityIndicator />;
|
2015-03-11 02:11:28 +00:00
|
|
|
}
|
|
|
|
return null;
|
|
|
|
},
|
|
|
|
|
|
|
|
// rowData is an array of images
|
2018-05-11 20:32:37 +00:00
|
|
|
_renderRow: function(
|
|
|
|
rowData: Array<Image>,
|
|
|
|
sectionID: string,
|
|
|
|
rowID: string,
|
|
|
|
) {
|
|
|
|
var images = rowData.map(image => {
|
2015-03-11 02:11:28 +00:00
|
|
|
if (image === null) {
|
|
|
|
return null;
|
|
|
|
}
|
2017-03-05 03:29:24 +00:00
|
|
|
// $FlowFixMe(>=0.41.0)
|
2015-03-11 02:11:28 +00:00
|
|
|
return this.props.renderImage(image);
|
|
|
|
});
|
|
|
|
|
2018-05-11 20:32:37 +00:00
|
|
|
return <View style={styles.row}>{images}</View>;
|
2015-03-11 02:11:28 +00:00
|
|
|
},
|
|
|
|
|
2015-03-23 17:06:16 +00:00
|
|
|
_appendAssets: function(data: Object) {
|
2015-03-11 02:11:28 +00:00
|
|
|
var assets = data.edges;
|
2018-05-11 20:32:37 +00:00
|
|
|
var newState: Object = {loadingMore: false};
|
2015-03-11 02:11:28 +00:00
|
|
|
|
|
|
|
if (!data.page_info.has_next_page) {
|
|
|
|
newState.noMore = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (assets.length > 0) {
|
|
|
|
newState.lastCursor = data.page_info.end_cursor;
|
|
|
|
newState.assets = this.state.assets.concat(assets);
|
|
|
|
newState.dataSource = this.state.dataSource.cloneWithRows(
|
2017-03-05 03:29:24 +00:00
|
|
|
// $FlowFixMe(>=0.41.0)
|
2018-05-11 20:32:37 +00:00
|
|
|
groupByEveryN(newState.assets, this.props.imagesPerRow),
|
2015-03-11 02:11:28 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
this.setState(newState);
|
|
|
|
},
|
|
|
|
|
|
|
|
_onEndReached: function() {
|
|
|
|
if (!this.state.noMore) {
|
|
|
|
this.fetch();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
var styles = StyleSheet.create({
|
|
|
|
row: {
|
|
|
|
flexDirection: 'row',
|
|
|
|
flex: 1,
|
|
|
|
},
|
|
|
|
url: {
|
|
|
|
fontSize: 9,
|
|
|
|
marginBottom: 14,
|
|
|
|
},
|
|
|
|
image: {
|
|
|
|
margin: 4,
|
|
|
|
},
|
|
|
|
info: {
|
|
|
|
flex: 1,
|
|
|
|
},
|
|
|
|
container: {
|
|
|
|
flex: 1,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
module.exports = CameraRollView;
|