Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@furkancelik
Created September 21, 2017 17:52
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save furkancelik/168c2c7b0ddce86176eb242d4b416716 to your computer and use it in GitHub Desktop.
Save furkancelik/168c2c7b0ddce86176eb242d4b416716 to your computer and use it in GitHub Desktop.
/* @flow */
import React, { Component } from 'react';
import {
TouchableOpacity,
Image,
View,
Text,
StatusBar,
ScrollView,
Dimensions,
CameraRoll,
Platform,
FlatList,
Animated,
MaskedViewIOS,
InteractionManager,
ActivityIndicator,
ImageEditor,
} from 'react-native';
import { observer, inject } from 'mobx-react/native';
import Icon from 'react-native-vector-icons/Ionicons';
import Svg, { LinearGradient, Stop, Rect } from 'react-native-svg';
import ProfilePictureItem from '../../components/Camera/ProfilePictureItem';
import type { AppStore } from '../../types/stores/AppStore';
import type { RegisterStore } from '../../types/stores/RegisterStore';
const AFlatList: any = Animated.createAnimatedComponent(FlatList);
const { width }: { width: number } = Dimensions.get('window');
type State = {
page: number,
loading: boolean,
images: ?Object,
selectImage: ?Object,
selected: ?Object,
randomPhoto: ?string,
allImages: ?Array<any>,
};
type Props = {
navigation: Object,
registerStore: RegisterStore,
appStore: AppStore,
};
@inject('appStore', 'registerStore')
@observer
export default class Picker extends Component<void, Props, State> {
props: Props;
state: State;
scrollY: Object;
cropImage: Object;
imageH: number;
imageW: number;
imageRatio: number;
zoomScale: number;
static navigationOptions = {
header: false,
};
constructor(props: Props) {
super(props);
this.state = {
page: 0,
loading: false,
images: null,
selectImage: null,
selected: null,
randomPhoto: null,
allImages: null,
};
this.scrollY = new Animated.Value(0);
}
shouldComponentUpdate(nextProps: Props, nextState: State) {
if (
// nextState.selectImage !== this.state.selectImage ||
this.state.images !== nextState.images ||
this.state.randomPhoto !== nextState.randomPhoto
) {
return true;
}
return false;
}
componentWillMount() {
this.fetch();
}
fetch() {
const { appStore } = this.props;
if (appStore.getProfilePictureLibrary === null) {
const fetchParams: {
first: number,
groupTypes: string,
assetType: string,
} = {
first: 1000,
groupTypes: 'SavedPhotos',
assetType: 'Photos',
};
if (Platform.OS === 'android') {
delete fetchParams.groupTypes;
}
CameraRoll.getPhotos(fetchParams).then(
(data: Object) => this.appendImage((data: Object)),
(err: ?Object | ?string) => {
// console.log(err);
},
);
} else {
const allImages: Array<any> = this.nEveryRow(appStore.getProfilePictureLibrary.edges, 100);
appStore.setSelectProfilePicture(appStore.getProfilePictureLibrary.edges[0]);
const images: Object = allImages[0];
this.setState({
selected: appStore.getProfilePictureLibrary.edges[0],
selectImage: appStore.getProfilePictureLibrary.edges[0],
images,
allImages,
loading: true,
});
}
}
appendImage(data: { edges: Array<any> }) {
const { appStore } = this.props;
const allImages: Array<any> = this.nEveryRow(data.edges, 100);
appStore.setProfilePictureLibrary(data);
appStore.setSelectProfilePicture(data.edges[0]);
const images: Object = allImages[0];
this.setState({
selected: data.edges[0],
selectImage: data.edges[0],
images,
allImages,
loading: true,
});
}
nEveryRow(data: Array<any>, n: number): Array<any> {
const result: Array<any> = [];
let temp: Array<any> = [];
for (let i = 0; i < data.length; ++i) {
if (i > 0 && i % n === 0) {
result.push(temp);
temp = [];
}
temp.push(data[i]);
}
if (temp.length > 0) {
result.push(temp);
}
return result;
}
setAnimate() {
Animated.timing(this.scrollY, {
toValue: 0,
duration: 300,
useNativeDriver: true,
}).start();
}
selectImage(image: Object) {
this.setState({ selectImage: image }, this.setAnimate());
}
gridRender(color: string = 'rgba(255,255,255,0.5)', lineSize: number = 0.5) {
return (
<View style={{ position: 'absolute', zIndex: 1 }}>
<View
style={{
position: 'absolute',
width,
height: lineSize,
backgroundColor: color,
top: width / 3 * 2,
}}
/>
<View
style={{
position: 'absolute',
width,
height: lineSize,
backgroundColor: color,
top: width / 3,
}}
/>
<View
style={{
position: 'absolute',
width: lineSize,
height: width,
backgroundColor: color,
top: 0,
left: width / 3,
}}
/>
<View
style={{
position: 'absolute',
width: lineSize,
height: width,
backgroundColor: color,
top: 0,
left: width / 3 * 2,
}}
/>
</View>
);
}
imageRender(image: { width: number, height: number, uri: string }) {
let imageHeight: number = width;
let imageWidth: number = width;
let ratio: number = 1;
if (image.width > image.height) {
ratio = image.height / width;
imageHeight = width;
imageWidth = image.width / ratio;
} else {
ratio = image.width / width;
imageHeight = image.height / ratio;
imageWidth = width;
}
return (
<Image
ref={(c: Object) => {
this.cropImage = c;
this.imageH = imageHeight;
this.imageW = imageWidth;
this.imageRatio = ratio;
}}
resizeMode={'contain'}
source={{ uri: image.uri }}
style={{
height: imageHeight,
width: imageWidth,
}}
/>
);
}
render() {
const { appStore, registerStore, navigation } = this.props;
if (this.state.randomPhoto) {
return (
<View style={{ flex: 1, marginTop: 50 }}>
<Image
source={{ uri: this.state.randomPhoto }}
style={{
width,
height: width,
borderRadius: width / 2,
}}
/>
<TouchableOpacity
onPress={() => {
this.zoomScale = 1;
this.setState({ randomPhoto: null });
}}
>
<Text>iptal</Text>
</TouchableOpacity>
</View>
);
}
if (!this.state.loading) {
return (
<View
style={{
backgroundColor: '#000',
justifyContent: 'center',
alignItems: 'center',
flex: 1,
}}
>
<StatusBar hidden />
<ActivityIndicator
animating
style={{ alignItems: 'center', justifyContent: 'center', height: 80 }}
size="large"
/>
<Text style={{ color: '#fff', textAlign: 'center', fontSize: 15, fontWeight: '600' }}>
Resimleriniz Yükleniyor {'\n'} Lütfen Bekleyiniz
</Text>
</View>
);
}
if (this.state.images.length < 1) {
return (
<View
style={{
backgroundColor: '#000',
justifyContent: 'center',
alignItems: 'center',
flex: 1,
}}
>
<StatusBar hidden />
<Text style={{ color: '#fff', textAlign: 'center', fontSize: 15, fontWeight: '600' }}>
Galeride Resim Bulunamadı
</Text>
</View>
);
}
const imageTranslate: Object = this.scrollY.interpolate({
inputRange: [0, width],
outputRange: [0, -width],
extrapolate: 'clamp',
});
return (
<View style={{ flex: 1, backgroundColor: '#000' }}>
<StatusBar hidden />
<AFlatList
onEndReached={() => {
const page: number = this.state.page + 1;
if (page < this.state.allImages.length) {
const images: Object = [...this.state.images, ...this.state.allImages[page]];
this.setState({
page,
images,
});
}
}}
contentContainerStyle={{
marginTop: width + 50,
paddingBottom: width + 50,
}}
numColumns={4}
keyExtractor={(item: { node: { image: { uri: string } } }) => item.node.image.uri}
data={this.state.images}
scrollEventThrottle={1}
style={{ flex: 1, backgroundColor: '#000', paddingLeft: 2 }}
onScroll={Animated.event([{ nativeEvent: { contentOffset: { y: this.scrollY } } }], {
useNativeDriver: true,
})}
renderItem={({ item }: { item: Object }) =>
(<ProfilePictureItem
selectImage={(image: Object) => {
// InteractionManager.runAfterInteractions(() => {
appStore.setSelectProfilePicture(image);
this.setAnimate();
// });
}}
selectImageItem={appStore.getSelectProfilePicture}
item={item}
/>)}
/>
<Animated.View
style={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
backgroundColor: '#000',
overflow: 'hidden',
transform: [{ translateY: imageTranslate }],
}}
>
<Svg width={width} height="50">
<LinearGradient id="grad" x1="0%" y1="0%" x2="0%" y2="100%">
<Stop offset="0" stopColor="#7000e3" stopOpacity="1" />
<Stop offset="1" stopColor="#e836a8" stopOpacity="1" />
</LinearGradient>
<Rect x="0" y="0" width={width} height="50" fill="url(#grad)" />
<View
style={{
flex: 1,
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'row',
}}
>
<View style={{ flex: 0 }}>
<TouchableOpacity
onPress={() => navigation.goBack()}
style={{ paddingTop: 5, paddingLeft: 10, paddingRight: 15 }}
>
<Icon name="ios-close" size={35} color="#FFF" />
</TouchableOpacity>
</View>
<View
style={{
flex: 1,
justifyContent: 'center',
alignItems: 'center',
}}
>
<Text
style={{
flex: 0,
color: '#FFF',
fontSize: 17,
fontWeight: '500',
paddingTop: 5,
}}
>
Foto Galeri
</Text>
</View>
<View style={{ flex: 0 }}>
<TouchableOpacity
onPress={() => {
this.cropImage.measure((x, y, owidth, oheight, pageX, pageY) => {
pageY = (pageY - 50) * -1; // offset top header bar heih 50px
pageX *= -1;
const zoomScale: number = this.zoomScale || 1;
const w: number = this.imageW * this.imageRatio;
const h: number = this.imageH * this.imageRatio;
const _x: number =
(width - this.imageW) / 2 * this.imageRatio + pageX * this.imageRatio;
const _y: number =
(width - this.imageH) / 2 * this.imageRatio + pageY * this.imageRatio;
ImageEditor.cropImage(
appStore.selectProfilePicture.node.image.uri,
{
offset: {
x: _x / zoomScale,
y: _y / zoomScale,
},
size: {
width: w / zoomScale,
height: h / zoomScale,
},
resizeMode: 'cover',
},
(uri: string) => {
registerStore.setProfilePicture(uri);
navigation.goBack();
// back page
},
(e: Object) => {
// console.log(e)
},
);
});
}}
style={{ paddingTop: 5, paddingLeft: 15, paddingRight: 10 }}
>
<Icon name="ios-checkmark" size={35} color="#FFF" />
</TouchableOpacity>
</View>
</View>
</Svg>
<View
onStartShouldSetResponder={(e: Object) => {
if (width - 50 <= e.nativeEvent.locationY) {
this.setAnimate();
}
}}
style={{
width,
height: width,
marginBottom: 4,
}}
>
{this.gridRender()}
<MaskedViewIOS
style={{ flex: 1 }}
maskElement={
<View
style={{
flex: 1,
backgroundColor: 'rgba(0,0,0,0.4)',
alignItems: 'center',
justifyContent: 'center',
}}
>
<View
style={{
width,
height: width,
borderRadius: width / 2,
backgroundColor: '#FFF',
}}
/>
</View>
}
>
<ScrollView
showsHorizontalScrollIndicator={false}
showsVerticalScrollIndicator={false}
horizontal
contentContainerStyle={{
alignItems: 'center',
justifyContent: 'center',
}}
>
<ScrollView
onScroll={(e: Object) => {
this.zoomScale = e.nativeEvent.zoomScale;
}}
scrollEventThrottle={16}
showsHorizontalScrollIndicator={false}
showsVerticalScrollIndicator={false}
contentContainerStyle={{
alignItems: 'center',
justifyContent: 'center',
}}
maximumZoomScale={3}
minimumZoomScale={1}
>
{this.imageRender(appStore.getSelectProfilePicture.node.image)}
</ScrollView>
</ScrollView>
</MaskedViewIOS>
</View>
</Animated.View>
</View>
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment