├── .expo-shared
└── assets.json
├── .gitignore
├── App.js
├── README.md
├── app.json
├── app
├── components
│ ├── NotFound.js
│ ├── Note.js
│ ├── NoteDetail.js
│ ├── NoteInputModal.js
│ ├── RoundIconBtn.js
│ └── SearchBar.js
├── contexts
│ └── NoteProvider.js
├── misc
│ └── colors.js
└── screens
│ ├── Intro.js
│ └── NoteScreen.js
├── assets
├── adaptive-icon.png
├── favicon.png
├── icon.png
├── ndp_splash.png
└── splash.png
├── babel.config.js
├── package-lock.json
└── package.json
/.expo-shared/assets.json:
--------------------------------------------------------------------------------
1 | {
2 | "12bb71342c6255bbf50437ec8f4441c083f47cdb74bd89160c15e4f43e52a1cb": true,
3 | "40b842e832070c58deac6aa9e08fa459302ee3f9da492c7e77d93d2fbf4a56fd": true
4 | }
5 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/**/*
2 | .expo/*
3 | npm-debug.*
4 | *.jks
5 | *.p8
6 | *.p12
7 | *.key
8 | *.mobileprovision
9 | *.orig.*
10 | web-build/
11 |
12 | # macOS
13 | .DS_Store
14 |
--------------------------------------------------------------------------------
/App.js:
--------------------------------------------------------------------------------
1 | import AsyncStorage from '@react-native-async-storage/async-storage';
2 | import { StatusBar } from 'expo-status-bar';
3 | import React, { useEffect, useState } from 'react';
4 | import { StyleSheet, Text, View } from 'react-native';
5 | import { createStackNavigator } from '@react-navigation/stack';
6 | import { NavigationContainer } from '@react-navigation/native';
7 |
8 | import Intro from './app/screens/Intro';
9 | import NoteScreen from './app/screens/NoteScreen';
10 | import NoteDetail from './app/components/NoteDetail';
11 | import NoteProvider from './app/contexts/NoteProvider';
12 |
13 | const Stack = createStackNavigator();
14 |
15 | export default function App() {
16 | const [user, setUser] = useState({});
17 | const [isAppFirstTimeOpen, setIsAppFirstTimeOpen] = useState(false);
18 | const findUser = async () => {
19 | const result = await AsyncStorage.getItem('user');
20 |
21 | if (result === null) return setIsAppFirstTimeOpen(true);
22 |
23 | setUser(JSON.parse(result));
24 | setIsAppFirstTimeOpen(false);
25 | };
26 |
27 | useEffect(() => {
28 | findUser();
29 | }, []);
30 |
31 | const renderNoteScreen = props => ;
32 |
33 | if (isAppFirstTimeOpen) return ;
34 | return (
35 |
36 |
37 |
40 |
41 |
42 |
43 |
44 |
45 | );
46 | }
47 |
48 | const styles = StyleSheet.create({
49 | container: {
50 | flex: 1,
51 | backgroundColor: '#fff',
52 | alignItems: 'center',
53 | justifyContent: 'center',
54 | },
55 | });
56 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ndpniraj/react-native-async-storage-note-app/037b43b3a3e993f19506f0db9d4453f38272d777/README.md
--------------------------------------------------------------------------------
/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "expo": {
3 | "name": "ndp-note",
4 | "slug": "ndp-note",
5 | "version": "1.0.0",
6 | "orientation": "portrait",
7 | "icon": "./assets/icon.png",
8 | "splash": {
9 | "image": "./assets/ndp_splash.png",
10 | "resizeMode": "cover",
11 | "backgroundColor": "#ffffff"
12 | },
13 | "updates": {
14 | "fallbackToCacheTimeout": 0
15 | },
16 | "assetBundlePatterns": ["**/*"],
17 | "ios": {
18 | "supportsTablet": true
19 | },
20 | "android": {
21 | "adaptiveIcon": {
22 | "foregroundImage": "./assets/adaptive-icon.png",
23 | "backgroundColor": "#FFFFFF"
24 | }
25 | },
26 | "web": {
27 | "favicon": "./assets/favicon.png"
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/app/components/NotFound.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { View, StyleSheet, Text } from 'react-native';
3 | import { AntDesign } from '@expo/vector-icons';
4 |
5 | const NotFound = () => {
6 | return (
7 |
8 |
9 | Result Not Found
10 |
11 | );
12 | };
13 |
14 | const styles = StyleSheet.create({
15 | container: {
16 | flex: 1,
17 | justifyContent: 'center',
18 | alignItems: 'center',
19 | opacity: 0.5,
20 | zIndex: -1,
21 | },
22 | });
23 |
24 | export default NotFound;
25 |
--------------------------------------------------------------------------------
/app/components/Note.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | View,
4 | StyleSheet,
5 | Text,
6 | Dimensions,
7 | TouchableOpacity,
8 | } from 'react-native';
9 | import colors from '../misc/colors';
10 |
11 | const Note = ({ item, onPress }) => {
12 | const { title, desc } = item;
13 | return (
14 |
15 |
16 | {title}
17 |
18 | {desc}
19 |
20 | );
21 | };
22 |
23 | const width = Dimensions.get('window').width - 40;
24 |
25 | const styles = StyleSheet.create({
26 | container: {
27 | backgroundColor: colors.PRIMARY,
28 | width: width / 2 - 10,
29 | padding: 8,
30 | borderRadius: 10,
31 | },
32 | title: {
33 | fontWeight: 'bold',
34 | fontSize: 16,
35 | color: colors.LIGHT,
36 | },
37 | });
38 |
39 | export default Note;
40 |
--------------------------------------------------------------------------------
/app/components/NoteDetail.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import { ScrollView, StyleSheet, Text, View, Alert } from 'react-native';
3 | import { useHeaderHeight } from '@react-navigation/stack';
4 | import colors from '../misc/colors';
5 | import RoundIconBtn from './RoundIconBtn';
6 | import AsyncStorage from '@react-native-async-storage/async-storage';
7 | import { useNotes } from '../contexts/NoteProvider';
8 | import NoteInputModal from './NoteInputModal';
9 |
10 | const formatDate = ms => {
11 | const date = new Date(ms);
12 | const day = date.getDate();
13 | const month = date.getMonth() + 1;
14 | const year = date.getFullYear();
15 | const hrs = date.getHours();
16 | const min = date.getMinutes();
17 | const sec = date.getSeconds();
18 |
19 | return `${day}/${month}/${year} - ${hrs}:${min}:${sec}`;
20 | };
21 |
22 | const NoteDetail = props => {
23 | const [note, setNote] = useState(props.route.params.note);
24 | const headerHeight = useHeaderHeight();
25 | const { setNotes } = useNotes();
26 | const [showModal, setShowModal] = useState(false);
27 | const [isEdit, setIsEdit] = useState(false);
28 |
29 | const deleteNote = async () => {
30 | const result = await AsyncStorage.getItem('notes');
31 | let notes = [];
32 | if (result !== null) notes = JSON.parse(result);
33 |
34 | const newNotes = notes.filter(n => n.id !== note.id);
35 | setNotes(newNotes);
36 | await AsyncStorage.setItem('notes', JSON.stringify(newNotes));
37 | props.navigation.goBack();
38 | };
39 |
40 | const displayDeleteAlert = () => {
41 | Alert.alert(
42 | 'Are You Sure!',
43 | 'This action will delete your note permanently!',
44 | [
45 | {
46 | text: 'Delete',
47 | onPress: deleteNote,
48 | },
49 | {
50 | text: 'No Thanks',
51 | onPress: () => console.log('no thanks'),
52 | },
53 | ],
54 | {
55 | cancelable: true,
56 | }
57 | );
58 | };
59 |
60 | const handleUpdate = async (title, desc, time) => {
61 | const result = await AsyncStorage.getItem('notes');
62 | let notes = [];
63 | if (result !== null) notes = JSON.parse(result);
64 |
65 | const newNotes = notes.filter(n => {
66 | if (n.id === note.id) {
67 | n.title = title;
68 | n.desc = desc;
69 | n.isUpdated = true;
70 | n.time = time;
71 |
72 | setNote(n);
73 | }
74 | return n;
75 | });
76 |
77 | setNotes(newNotes);
78 | await AsyncStorage.setItem('notes', JSON.stringify(newNotes));
79 | };
80 | const handleOnClose = () => setShowModal(false);
81 |
82 | const openEditModal = () => {
83 | setIsEdit(true);
84 | setShowModal(true);
85 | };
86 |
87 | return (
88 | <>
89 |
92 |
93 | {note.isUpdated
94 | ? `Updated At ${formatDate(note.time)}`
95 | : `Created At ${formatDate(note.time)}`}
96 |
97 | {note.title}
98 | {note.desc}
99 |
100 |
101 |
106 |
107 |
108 |
115 | >
116 | );
117 | };
118 |
119 | const styles = StyleSheet.create({
120 | container: {
121 | // flex: 1,
122 | paddingHorizontal: 15,
123 | },
124 | title: {
125 | fontSize: 30,
126 | color: colors.PRIMARY,
127 | fontWeight: 'bold',
128 | },
129 | desc: {
130 | fontSize: 20,
131 | opacity: 0.6,
132 | },
133 | time: {
134 | textAlign: 'right',
135 | fontSize: 12,
136 | opacity: 0.5,
137 | },
138 | btnContainer: {
139 | position: 'absolute',
140 | right: 15,
141 | bottom: 50,
142 | },
143 | });
144 |
145 | export default NoteDetail;
146 |
--------------------------------------------------------------------------------
/app/components/NoteInputModal.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from 'react';
2 | import {
3 | View,
4 | StyleSheet,
5 | Modal,
6 | Text,
7 | StatusBar,
8 | TextInput,
9 | TouchableWithoutFeedback,
10 | Keyboard,
11 | } from 'react-native';
12 | import colors from '../misc/colors';
13 | import RoundIconBtn from './RoundIconBtn';
14 |
15 | const NoteInputModal = ({ visible, onClose, onSubmit, note, isEdit }) => {
16 | const [title, setTitle] = useState('');
17 | const [desc, setDesc] = useState('');
18 | const handleModalClose = () => {
19 | Keyboard.dismiss();
20 | };
21 |
22 | useEffect(() => {
23 | if (isEdit) {
24 | setTitle(note.title);
25 | setDesc(note.desc);
26 | }
27 | }, [isEdit]);
28 |
29 | const handleOnChangeText = (text, valueFor) => {
30 | if (valueFor === 'title') setTitle(text);
31 | if (valueFor === 'desc') setDesc(text);
32 | };
33 |
34 | const handleSubmit = () => {
35 | if (!title.trim() && !desc.trim()) return onClose();
36 |
37 | if (isEdit) {
38 | onSubmit(title, desc, Date.now());
39 | } else {
40 | onSubmit(title, desc);
41 | setTitle('');
42 | setDesc('');
43 | }
44 | onClose();
45 | };
46 |
47 | const closeModal = () => {
48 | if (!isEdit) {
49 | setTitle('');
50 | setDesc('');
51 | }
52 | onClose();
53 | };
54 |
55 | return (
56 | <>
57 |
58 |
59 |
60 | handleOnChangeText(text, 'title')}
63 | placeholder='Title'
64 | style={[styles.input, styles.title]}
65 | />
66 | handleOnChangeText(text, 'desc')}
72 | />
73 |
74 |
79 | {title.trim() || desc.trim() ? (
80 |
86 | ) : null}
87 |
88 |
89 |
90 |
91 |
92 |
93 | >
94 | );
95 | };
96 |
97 | const styles = StyleSheet.create({
98 | container: {
99 | paddingHorizontal: 20,
100 | paddingTop: 15,
101 | },
102 | input: {
103 | borderBottomWidth: 2,
104 | borderBottomColor: colors.PRIMARY,
105 | fontSize: 20,
106 | color: colors.DARK,
107 | },
108 | title: {
109 | height: 40,
110 | marginBottom: 15,
111 | fontWeight: 'bold',
112 | },
113 | desc: {
114 | height: 100,
115 | },
116 | modalBG: {
117 | flex: 1,
118 | zIndex: -1,
119 | },
120 | btnContainer: {
121 | flexDirection: 'row',
122 | justifyContent: 'center',
123 | paddingVertical: 15,
124 | },
125 | });
126 |
127 | export default NoteInputModal;
128 |
--------------------------------------------------------------------------------
/app/components/RoundIconBtn.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { View, StyleSheet } from 'react-native';
3 | import { AntDesign } from '@expo/vector-icons';
4 | import colors from '../misc/colors';
5 |
6 | const RoundIconBtn = ({ antIconName, size, color, style, onPress }) => {
7 | return (
8 |
15 | );
16 | };
17 |
18 | const styles = StyleSheet.create({
19 | icon: {
20 | backgroundColor: colors.PRIMARY,
21 | padding: 15,
22 | borderRadius: 50,
23 | elevation: 5,
24 | },
25 | });
26 |
27 | export default RoundIconBtn;
28 |
--------------------------------------------------------------------------------
/app/components/SearchBar.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { View, StyleSheet, TextInput } from 'react-native';
3 | import { AntDesign } from '@expo/vector-icons';
4 | import colors from '../misc/colors';
5 |
6 | const SearchBar = ({ containerStyle, value, onClear, onChangeText }) => {
7 | return (
8 |
9 |
15 | {value ? (
16 |
23 | ) : null}
24 |
25 | );
26 | };
27 |
28 | const styles = StyleSheet.create({
29 | searchBar: {
30 | borderWidth: 0.5,
31 | borderColor: colors.PRIMARY,
32 | height: 40,
33 | borderRadius: 40,
34 | paddingLeft: 15,
35 | fontSize: 20,
36 | },
37 | container: {
38 | justifyContent: 'center',
39 | },
40 | clearIcon: {
41 | position: 'absolute',
42 | right: 10,
43 | },
44 | });
45 |
46 | export default SearchBar;
47 |
--------------------------------------------------------------------------------
/app/contexts/NoteProvider.js:
--------------------------------------------------------------------------------
1 | import AsyncStorage from '@react-native-async-storage/async-storage';
2 | import React, { createContext, useContext, useEffect, useState } from 'react';
3 |
4 | const NoteContext = createContext();
5 | const NoteProvider = ({ children }) => {
6 | const [notes, setNotes] = useState([]);
7 |
8 | const findNotes = async () => {
9 | const result = await AsyncStorage.getItem('notes');
10 | if (result !== null) setNotes(JSON.parse(result));
11 | };
12 |
13 | useEffect(() => {
14 | findNotes();
15 | }, []);
16 |
17 | return (
18 |
19 | {children}
20 |
21 | );
22 | };
23 |
24 | export const useNotes = () => useContext(NoteContext);
25 |
26 | export default NoteProvider;
27 |
--------------------------------------------------------------------------------
/app/misc/colors.js:
--------------------------------------------------------------------------------
1 | export default {
2 | DARK: '#1e1e1e',
3 | LIGHT: '#FFF',
4 | PRIMARY: '#dbb2ff',
5 | ERROR: '#ff0000',
6 | };
7 |
--------------------------------------------------------------------------------
/app/screens/Intro.js:
--------------------------------------------------------------------------------
1 | import AsyncStorage from '@react-native-async-storage/async-storage';
2 | import React, { useState } from 'react';
3 | import {
4 | View,
5 | StyleSheet,
6 | Text,
7 | TextInput,
8 | StatusBar,
9 | Dimensions,
10 | } from 'react-native';
11 | import RoundIconBtn from '../components/RoundIconBtn';
12 | import colors from '../misc/colors';
13 |
14 | const Intro = ({ onFinish }) => {
15 | const [name, setName] = useState('');
16 | const handleOnChangeText = text => setName(text);
17 |
18 | const handleSubmit = async () => {
19 | const user = { name: name };
20 | await AsyncStorage.setItem('user', JSON.stringify(user));
21 | if (onFinish) onFinish();
22 | };
23 |
24 | return (
25 | <>
26 |
27 |
28 | Enter Your Name to Continue
29 |
35 | {name.trim().length >= 3 ? (
36 |
37 | ) : null}
38 |
39 | >
40 | );
41 | };
42 |
43 | const width = Dimensions.get('window').width - 50;
44 | const styles = StyleSheet.create({
45 | container: {
46 | flex: 1,
47 | justifyContent: 'center',
48 | alignItems: 'center',
49 | },
50 | textInput: {
51 | borderWidth: 2,
52 | borderColor: colors.PRIMARY,
53 | color: colors.PRIMARY,
54 | width,
55 | height: 50,
56 | borderRadius: 10,
57 | paddingLeft: 15,
58 | fontSize: 25,
59 | marginBottom: 15,
60 | },
61 | inputTitle: {
62 | alignSelf: 'flex-start',
63 | paddingLeft: 25,
64 | marginBottom: 5,
65 | opacity: 0.5,
66 | },
67 | });
68 |
69 | export default Intro;
70 |
--------------------------------------------------------------------------------
/app/screens/NoteScreen.js:
--------------------------------------------------------------------------------
1 | import AsyncStorage from '@react-native-async-storage/async-storage';
2 | import React, { useEffect, useState } from 'react';
3 | import {
4 | View,
5 | StyleSheet,
6 | Text,
7 | StatusBar,
8 | TouchableWithoutFeedback,
9 | Keyboard,
10 | FlatList,
11 | } from 'react-native';
12 | import Note from '../components/Note';
13 | import NoteInputModal from '../components/NoteInputModal';
14 | import NotFound from '../components/NotFound';
15 | import RoundIconBtn from '../components/RoundIconBtn';
16 | import SearchBar from '../components/SearchBar';
17 | import { useNotes } from '../contexts/NoteProvider';
18 | import colors from '../misc/colors';
19 |
20 | const reverseData = data => {
21 | return data.sort((a, b) => {
22 | const aInt = parseInt(a.time);
23 | const bInt = parseInt(b.time);
24 | if (aInt < bInt) return 1;
25 | if (aInt == bInt) return 0;
26 | if (aInt > bInt) return -1;
27 | });
28 | };
29 |
30 | const NoteScreen = ({ user, navigation }) => {
31 | const [greet, setGreet] = useState('');
32 | const [modalVisible, setModalVisible] = useState(false);
33 | const [searchQuery, setSearchQuery] = useState('');
34 | const [resultNotFound, setResultNotFound] = useState(false);
35 |
36 | const { notes, setNotes, findNotes } = useNotes();
37 |
38 | const findGreet = () => {
39 | const hrs = new Date().getHours();
40 | if (hrs === 0 || hrs < 12) return setGreet('Morning');
41 | if (hrs === 1 || hrs < 17) return setGreet('Afternoon');
42 | setGreet('Evening');
43 | };
44 |
45 | useEffect(() => {
46 | findGreet();
47 | }, []);
48 |
49 | const reverseNotes = reverseData(notes);
50 |
51 | const handleOnSubmit = async (title, desc) => {
52 | const note = { id: Date.now(), title, desc, time: Date.now() };
53 | const updatedNotes = [...notes, note];
54 | setNotes(updatedNotes);
55 | await AsyncStorage.setItem('notes', JSON.stringify(updatedNotes));
56 | };
57 |
58 | const openNote = note => {
59 | navigation.navigate('NoteDetail', { note });
60 | };
61 |
62 | const handleOnSearchInput = async text => {
63 | setSearchQuery(text);
64 | if (!text.trim()) {
65 | setSearchQuery('');
66 | setResultNotFound(false);
67 | return await findNotes();
68 | }
69 | const filteredNotes = notes.filter(note => {
70 | if (note.title.toLowerCase().includes(text.toLowerCase())) {
71 | return note;
72 | }
73 | });
74 |
75 | if (filteredNotes.length) {
76 | setNotes([...filteredNotes]);
77 | } else {
78 | setResultNotFound(true);
79 | }
80 | };
81 |
82 | const handleOnClear = async () => {
83 | setSearchQuery('');
84 | setResultNotFound(false);
85 | await findNotes();
86 | };
87 |
88 | return (
89 | <>
90 |
91 |
92 |
93 | {`Good ${greet} ${user.name}`}
94 | {notes.length ? (
95 |
101 | ) : null}
102 |
103 | {resultNotFound ? (
104 |
105 | ) : (
106 | item.id.toString()}
114 | renderItem={({ item }) => (
115 | openNote(item)} item={item} />
116 | )}
117 | />
118 | )}
119 |
120 | {!notes.length ? (
121 |
127 | Add Notes
128 |
129 | ) : null}
130 |
131 |
132 | setModalVisible(true)}
134 | antIconName='plus'
135 | style={styles.addBtn}
136 | />
137 | setModalVisible(false)}
140 | onSubmit={handleOnSubmit}
141 | />
142 | >
143 | );
144 | };
145 |
146 | const styles = StyleSheet.create({
147 | header: {
148 | fontSize: 25,
149 | fontWeight: 'bold',
150 | },
151 | container: {
152 | paddingHorizontal: 20,
153 | flex: 1,
154 | zIndex: 1,
155 | },
156 | emptyHeader: {
157 | fontSize: 30,
158 | textTransform: 'uppercase',
159 | fontWeight: 'bold',
160 | opacity: 0.2,
161 | },
162 | emptyHeaderContainer: {
163 | flex: 1,
164 | justifyContent: 'center',
165 | alignItems: 'center',
166 | zIndex: -1,
167 | },
168 | addBtn: {
169 | position: 'absolute',
170 | right: 15,
171 | bottom: 50,
172 | zIndex: 1,
173 | },
174 | });
175 |
176 | export default NoteScreen;
177 |
--------------------------------------------------------------------------------
/assets/adaptive-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ndpniraj/react-native-async-storage-note-app/037b43b3a3e993f19506f0db9d4453f38272d777/assets/adaptive-icon.png
--------------------------------------------------------------------------------
/assets/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ndpniraj/react-native-async-storage-note-app/037b43b3a3e993f19506f0db9d4453f38272d777/assets/favicon.png
--------------------------------------------------------------------------------
/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ndpniraj/react-native-async-storage-note-app/037b43b3a3e993f19506f0db9d4453f38272d777/assets/icon.png
--------------------------------------------------------------------------------
/assets/ndp_splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ndpniraj/react-native-async-storage-note-app/037b43b3a3e993f19506f0db9d4453f38272d777/assets/ndp_splash.png
--------------------------------------------------------------------------------
/assets/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ndpniraj/react-native-async-storage-note-app/037b43b3a3e993f19506f0db9d4453f38272d777/assets/splash.png
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = function(api) {
2 | api.cache(true);
3 | return {
4 | presets: ['babel-preset-expo'],
5 | };
6 | };
7 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "main": "node_modules/expo/AppEntry.js",
3 | "scripts": {
4 | "start": "expo start",
5 | "android": "expo start --android",
6 | "ios": "expo start --ios",
7 | "web": "expo start --web",
8 | "eject": "expo eject"
9 | },
10 | "dependencies": {
11 | "@react-native-async-storage/async-storage": "^1.14.1",
12 | "@react-native-community/masked-view": "0.1.10",
13 | "@react-navigation/native": "^5.9.3",
14 | "@react-navigation/stack": "^5.14.3",
15 | "expo": "~40.0.0",
16 | "expo-status-bar": "~1.0.3",
17 | "react": "16.13.1",
18 | "react-dom": "16.13.1",
19 | "react-native": "https://github.com/expo/react-native/archive/sdk-40.0.1.tar.gz",
20 | "react-native-gesture-handler": "~1.8.0",
21 | "react-native-reanimated": "~1.13.0",
22 | "react-native-safe-area-context": "3.1.9",
23 | "react-native-screens": "~2.15.2",
24 | "react-native-web": "~0.13.12"
25 | },
26 | "devDependencies": {
27 | "@babel/core": "~7.9.0"
28 | },
29 | "private": true
30 | }
31 |
--------------------------------------------------------------------------------