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