1import { useState, useRef } from 'react'; 2import { StatusBar } from 'expo-status-bar'; 3import { StyleSheet, View } from 'react-native'; 4import * as ImagePicker from 'expo-image-picker'; 5import { GestureHandlerRootView } from 'react-native-gesture-handler'; 6import * as MediaLibrary from 'expo-media-library'; 7import { captureRef } from 'react-native-view-shot'; 8 9import Button from './components/Button'; 10import ImageViewer from './components/ImageViewer'; 11import CircleButton from './components/CircleButton'; 12import IconButton from './components/IconButton'; 13import EmojiPicker from './components/EmojiPicker'; 14import EmojiList from './components/EmojiList'; 15import EmojiSticker from './components/EmojiSticker'; 16 17const PlaceholderImage = require('./assets/images/background-image.png'); 18 19export default function App() { 20 const [isModalVisible, setIsModalVisible] = useState(false); 21 const [showAppOptions, setShowAppOptions] = useState(false); 22 const [pickedEmoji, setPickedEmoji] = useState(null); 23 const [selectedImage, setSelectedImage] = useState(null); 24 25 const [status, requestPermission] = MediaLibrary.usePermissions(); 26 const imageRef = useRef(); 27 28 if (status === null) { 29 requestPermission(); 30 } 31 32 const pickImageAsync = async () => { 33 let result = await ImagePicker.launchImageLibraryAsync({ 34 allowsEditing: true, 35 quality: 1, 36 }); 37 38 if (!result.canceled) { 39 setSelectedImage(result.assets[0].uri); 40 setShowAppOptions(true); 41 } else { 42 alert('You did not select any image.'); 43 } 44 }; 45 46 const onReset = () => { 47 setShowAppOptions(false); 48 }; 49 50 const onAddSticker = () => { 51 setIsModalVisible(true); 52 }; 53 54 const onModalClose = () => { 55 setIsModalVisible(false); 56 }; 57 58 59 const onSaveImageAsync = async () => { 60 try { 61 const localUri = await captureRef(imageRef, { 62 height: 440, 63 quality: 1, 64 }); 65 66 await MediaLibrary.saveToLibraryAsync(localUri); 67 if (localUri) { 68 alert("Saved!"); 69 } 70 } catch (e) { 71 console.log(e); 72 } 73 }; 74 75 return ( 76 <GestureHandlerRootView style={styles.container}> 77 <View style={styles.imageContainer}> 78 <View ref={imageRef} collapsable={false}> 79 <ImageViewer 80 ref={imageRef} 81 placeholderImageSource={PlaceholderImage} 82 selectedImage={selectedImage} 83 /> 84 {pickedEmoji !== null ? ( 85 <EmojiSticker imageSize={40} stickerSource={pickedEmoji} /> 86 ) : null} 87 </View> 88 </View> 89 {showAppOptions ? ( 90 <View style={styles.optionsContainer}> 91 <View style={styles.optionsRow}> 92 <IconButton icon="refresh" label="Reset" onPress={onReset} /> 93 <CircleButton onPress={onAddSticker} /> 94 <IconButton icon="save-alt" label="Save" onPress={onSaveImageAsync} /> 95 </View> 96 </View> 97 ) : ( 98 <View style={styles.footerContainer}> 99 <Button theme="primary" label="Choose a photo" onPress={pickImageAsync} /> 100 <Button 101 label="Use this photo" onPress={() => setShowAppOptions(true)} 102 /> 103 </View> 104 )} 105 <EmojiPicker isVisible={isModalVisible} onClose={onModalClose}> 106 <EmojiList onSelect={setPickedEmoji} onCloseModal={onModalClose} /> 107 </EmojiPicker> 108 <StatusBar style="auto" /> 109 </GestureHandlerRootView> 110 ); 111} 112 113const styles = StyleSheet.create({ 114 container: { 115 flex: 1, 116 backgroundColor: '#25292e', 117 alignItems: 'center', 118 }, 119 imageContainer: { 120 flex:1, 121 paddingTop: 58 122 }, 123 footerContainer: { 124 flex: 1 / 3, 125 alignItems: 'center', 126 }, 127 optionsContainer: { 128 position: 'absolute', 129 bottom: 80, 130 }, 131 optionsRow: { 132 alignItems: 'center', 133 flexDirection: 'row', 134 justifyContent: 'center', 135 }, 136}); 137