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