react-native/RNTester/js/CameraRollView.js

284 lines
6.4 KiB
JavaScript
Raw Normal View History

/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
'use strict';
var React = require('react');
var createReactClass = require('create-react-class');
const PropTypes = require('prop-types');
var ReactNative = require('react-native');
var {
ActivityIndicator,
Alert,
CameraRoll,
Image,
ListView,
PermissionsAndroid,
Platform,
StyleSheet,
View,
} = ReactNative;
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.
*/
groupTypes: PropTypes.oneOf([
'Album',
'All',
'Event',
'Faces',
'Library',
'PhotoStream',
'SavedPhotos',
]),
/**
* Number of images that will be fetched in one page.
*/
batchSize: PropTypes.number,
/**
* A function that takes a single image as a parameter and renders it.
*/
renderImage: PropTypes.func,
/**
* imagesPerRow: Number of images to be shown in each row.
*/
imagesPerRow: PropTypes.number,
/**
* The asset type, one of 'Photos', 'Videos' or 'All'
*/
assetType: PropTypes.oneOf([
'Photos',
'Videos',
'All',
]),
};
var CameraRollView = createReactClass({
displayName: 'CameraRollView',
propTypes: propTypes,
getDefaultProps: function(): Object {
return {
groupTypes: 'SavedPhotos',
batchSize: 5,
imagesPerRow: 1,
assetType: 'Photos',
renderImage: function(asset) {
var imageSize = 150;
var imageStyle = [styles.image, {width: imageSize, height: imageSize}];
return (
<Image
source={asset.node.image}
style={imageStyle}
/>
);
},
};
},
getInitialState: function() {
Updates from Thu 19 Mar - [ReactNative] Add root package.json name back | Tadeu Zagallo - [react-packager] Make sure projectRoots is converted to an array | Amjad Masad - [ReactNative] Init script that bootstraps new Xcode project | Alex Kotliarskyi - [ReactNative] New SampleApp | Alex Kotliarskyi - [ReactNative] Touchable invoke press on longPress when longPress handler missing | Eric Vicenti - [ReactNative] Commit missing RCTWebSocketDebugger.xcodeproj | Alex Kotliarskyi - [ReactNative] Add Custom Components folder | Christopher Chedeau - [react-packager] Hash cache file name information to avoid long names | Amjad Masad - [ReactNative] Put all iOS-related files in a subfolder | Alex Kotliarskyi - [react-packager] Fix OOM | Amjad Masad - [ReactNative] Bring Chrome debugger to OSS. Part 2 | Alex Kotliarskyi - [ReactNative] Add search to UIExplorer | Tadeu Zagallo - [ReactNative][RFC] Bring Chrome debugger to OSS. Part 1 | Alex Kotliarskyi - [ReactNative] Return the appropriate status code from XHR | Tadeu Zagallo - [ReactNative] Make JS stack traces in Xcode prettier | Alex Kotliarskyi - [ReactNative] Remove duplicate package.json with the same name | Christopher Chedeau - [ReactNative] Remove invariant from require('react-native') | Christopher Chedeau - [ReactNative] Remove ListViewDataSource from require('react-native') | Christopher Chedeau - [react-packager] Add assetRoots option | Amjad Masad - Convert UIExplorer to ListView | Christopher Chedeau - purge rni | Basil Hosmer - [ReactNative] s/render*View/render/ in <WebView> | Christopher Chedeau
2015-03-20 15:43:51 +00:00
var ds = new ListView.DataSource({rowHasChanged: this._rowHasChanged});
return {
assets: ([]: Array<Image>),
groupTypes: this.props.groupTypes,
lastCursor: (null : ?string),
assetType: this.props.assetType,
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() {
Updates from Thu 19 Mar - [ReactNative] Add root package.json name back | Tadeu Zagallo - [react-packager] Make sure projectRoots is converted to an array | Amjad Masad - [ReactNative] Init script that bootstraps new Xcode project | Alex Kotliarskyi - [ReactNative] New SampleApp | Alex Kotliarskyi - [ReactNative] Touchable invoke press on longPress when longPress handler missing | Eric Vicenti - [ReactNative] Commit missing RCTWebSocketDebugger.xcodeproj | Alex Kotliarskyi - [ReactNative] Add Custom Components folder | Christopher Chedeau - [react-packager] Hash cache file name information to avoid long names | Amjad Masad - [ReactNative] Put all iOS-related files in a subfolder | Alex Kotliarskyi - [react-packager] Fix OOM | Amjad Masad - [ReactNative] Bring Chrome debugger to OSS. Part 2 | Alex Kotliarskyi - [ReactNative] Add search to UIExplorer | Tadeu Zagallo - [ReactNative][RFC] Bring Chrome debugger to OSS. Part 1 | Alex Kotliarskyi - [ReactNative] Return the appropriate status code from XHR | Tadeu Zagallo - [ReactNative] Make JS stack traces in Xcode prettier | Alex Kotliarskyi - [ReactNative] Remove duplicate package.json with the same name | Christopher Chedeau - [ReactNative] Remove invariant from require('react-native') | Christopher Chedeau - [ReactNative] Remove ListViewDataSource from require('react-native') | Christopher Chedeau - [react-packager] Add assetRoots option | Amjad Masad - Convert UIExplorer to ListView | Christopher Chedeau - purge rni | Basil Hosmer - [ReactNative] s/render*View/render/ in <WebView> | Christopher Chedeau
2015-03-20 15:43:51 +00:00
var ds = new ListView.DataSource({rowHasChanged: this._rowHasChanged});
this.state.dataSource = ds.cloneWithRows(
// $FlowFixMe(>=0.41.0)
groupByEveryN(this.state.assets, this.props.imagesPerRow)
);
},
componentDidMount: function() {
this.fetch();
},
/* $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. */
UNSAFE_componentWillReceiveProps: function(nextProps: {groupTypes?: string}) {
if (this.props.groupTypes !== nextProps.groupTypes) {
this.fetch(true);
}
},
_fetch: async function(clear?: boolean) {
if (clear) {
this.setState(this.getInitialState(), this.fetch);
return;
}
if (Platform.OS === 'android') {
const result = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE,
{
title: 'Permission Explanation',
message: 'RNTester would like to access your pictures.',
},
);
if (result !== 'granted') {
Alert.alert('Access to pictures was denied.');
return;
}
}
const fetchParams: Object = {
first: this.props.batchSize,
groupTypes: this.props.groupTypes,
assetType: this.props.assetType,
};
if (Platform.OS === 'android') {
// not supported in android
delete fetchParams.groupTypes;
}
if (this.state.lastCursor) {
fetchParams.after = this.state.lastCursor;
}
try {
const data = await CameraRoll.getPhotos(fetchParams);
this._appendAssets(data);
} catch (e) {
logError(e);
}
},
/**
* 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.
*/
fetch: function(clear?: boolean) {
if (!this.state.loadingMore) {
this.setState({loadingMore: true}, () => { this._fetch(clear); });
}
},
render: function() {
return (
<ListView
renderRow={this._renderRow}
renderFooter={this._renderFooterSpinner}
onEndReached={this._onEndReached}
style={styles.container}
dataSource={this.state.dataSource}
enableEmptySections
/>
);
},
_rowHasChanged: function(r1: Array<Image>, r2: Array<Image>): boolean {
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) {
return <ActivityIndicator />;
}
return null;
},
// rowData is an array of images
_renderRow: function(rowData: Array<Image>, sectionID: string, rowID: string) {
var images = rowData.map((image) => {
if (image === null) {
return null;
}
// $FlowFixMe(>=0.41.0)
return this.props.renderImage(image);
});
return (
<View style={styles.row}>
{images}
</View>
);
},
_appendAssets: function(data: Object) {
var assets = data.edges;
var newState: Object = { loadingMore: false };
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(
// $FlowFixMe(>=0.41.0)
groupByEveryN(newState.assets, this.props.imagesPerRow)
);
}
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;