Created
November 19, 2018 05:52
Revisions
-
jittuu created this gist
Nov 19, 2018 .There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,193 @@ import React from 'react'; import { Dimensions, Image, ListRenderItemInfo, StyleSheet, Text, TouchableWithoutFeedback, View, ViewStyle, } from 'react-native'; import * as Animatable from 'react-native-animatable'; import firebase, { RNFirebase } from 'react-native-firebase'; import Progress from 'react-native-progress'; import { NavigationInjectedProps } from 'react-navigation'; import { FirebaseInfiniteFlatList } from 'src/components'; import { Photo, PhotoState } from 'src/store'; import theme from 'src/theme'; interface PhotoViewProps { photoState: PhotoState; no?: number; handler: (photoState: PhotoState) => void; } interface PhotoViewState { animation: string | undefined; } class PhotoView extends React.Component<PhotoViewProps, PhotoViewState> { constructor(props: PhotoViewProps) { super(props); this.state = { animation: undefined }; } onAnimationEnd = () => { this.setState({ animation: undefined }); } componentWillReceiveProps(nextProps: PhotoViewProps) { if (this.props.no !== nextProps.no) { this.setState({ animation: 'zoomIn' }); } } handlePress = () => { const { photoState, handler } = this.props; handler(photoState); } render() { const { photoState: item, no } = this.props; const src = item.status === 'uploading' ? item.path : (item.thumbnail || item.url); return ( <TouchableWithoutFeedback onPress={this.handlePress}> <View> <Image style={styles.image} source={{ uri: src }} /> {item.status === 'uploading' && <Progress.Circle style={styles.progress} progress={item.progress} thickness={4} />} <Animatable.View animation={this.state.animation} onAnimationEnd={this.onAnimationEnd} style={[styles.circle, !!no && styles.circleFill]} duration={300} useNativeDriver={true} > {!!no && <Text style={{ color: '#fff' }}>{no}</Text>} </Animatable.View> </View> </TouchableWithoutFeedback> ); } } class PhotoStateList extends FirebaseInfiniteFlatList<PhotoState> { } interface P extends NavigationInjectedProps { selectedPhotos: Photo[]; onChange: (selectedPhotos: Photo[]) => void; } export default class extends React.Component<P> { query: RNFirebase.firestore.Query; flatList: PhotoStateList | null; constructor(props: P) { super(props); this.flatList = null; this.query = firebase.firestore() .collection('photos') .orderBy('createdOn', 'desc'); this.state = { selectedPhotoIds: [] }; } onItemPress = ({ itemId }: { itemId: string }) => { const { navigation: { navigate } } = this.props; navigate('ItemDetails', { itemId }); } keyExtractor = (i: PhotoState) => i.id; docMapper = (doc: RNFirebase.firestore.DocumentSnapshot): PhotoState => { const { url, thumbnail, createdOn } = doc.data() as Photo; return { id: doc.id || 'UNKNOWN', status: 'uploaded', url, thumbnail, createdOn, }; } onPhotoSelect = (photoState: PhotoState) => { const { selectedPhotos, onChange } = this.props; if (selectedPhotos.some(sp => sp.id === photoState.id)) { onChange(selectedPhotos.filter(sp => sp.id !== photoState.id)); } else if (photoState.status === 'uploaded') { const { id, createdOn, thumbnail, url } = photoState; onChange(selectedPhotos.concat([{ id, url, thumbnail, createdOn, }])); } } refreshData = () => { if (this.flatList) { this.flatList.refreshData(); } } renderItem = ({ item }: ListRenderItemInfo<PhotoState>) => { const { selectedPhotos } = this.props; const foundIdx = selectedPhotos.findIndex(sp => sp.id === item.id); return ( <PhotoView photoState={item} no={foundIdx > -1 ? foundIdx + 1 : undefined} handler={this.onPhotoSelect} /> ); } render() { return ( <PhotoStateList ref={ls => this.flatList = ls} extraData={this.props.selectedPhotos} query={this.query} pageSize={100} keyExtractor={this.keyExtractor} renderItem={this.renderItem} docMapper={this.docMapper} numColumns={4} /> ); } } const { width } = Dimensions.get('window'); const styles = StyleSheet.create({ image: { marginLeft: 2, marginVertical: 2, width: ((width - 36) / 4), height: ((width - 36) / 4) * 1.2, } as ViewStyle, progress: { position: 'absolute', bottom: 10, right: 10, }, circle: { alignItems: 'center', justifyContent: 'center', width: 20, height: 20, borderRadius: 10, borderWidth: StyleSheet.hairlineWidth, borderColor: theme.primaryColor, position: 'absolute', top: 6, right: 4, } as ViewStyle, circleFill: { backgroundColor: theme.primaryColor, } as ViewStyle, });