├── .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 |