Skip to content

Instantly share code, notes, and snippets.

@jittuu
Created November 19, 2018 05:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jittuu/7f29b53fe6167799ee14f69299c811d4 to your computer and use it in GitHub Desktop.
Save jittuu/7f29b53fe6167799ee14f69299c811d4 to your computer and use it in GitHub Desktop.
usage of FirebaseInfiniteFlatList
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,
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment