├── assets ├── icon.png ├── favicon.png ├── splash.png └── adaptive-icon.png ├── images └── profile.png ├── babel.config.js ├── App.js ├── routes └── homeStack.js ├── app.json ├── package.json ├── screens ├── Home.js ├── ContactDetail.js └── ContactsList.js └── .gitignore /assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abbasikov/ContactApp/HEAD/assets/icon.png -------------------------------------------------------------------------------- /assets/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abbasikov/ContactApp/HEAD/assets/favicon.png -------------------------------------------------------------------------------- /assets/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abbasikov/ContactApp/HEAD/assets/splash.png -------------------------------------------------------------------------------- /images/profile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abbasikov/ContactApp/HEAD/images/profile.png -------------------------------------------------------------------------------- /assets/adaptive-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abbasikov/ContactApp/HEAD/assets/adaptive-icon.png -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function(api) { 2 | api.cache(true); 3 | return { 4 | presets: ['babel-preset-expo'], 5 | }; 6 | }; 7 | -------------------------------------------------------------------------------- /App.js: -------------------------------------------------------------------------------- 1 | import { StatusBar } from 'expo-status-bar'; 2 | import React from 'react'; 3 | import { StyleSheet, Text, View } from 'react-native'; 4 | import Navigator from './routes/homeStack' 5 | export default function App() { 6 | return ( 7 | 8 | ); 9 | } 10 | 11 | const styles = StyleSheet.create({ 12 | container: { 13 | flex: 1, 14 | backgroundColor: '#fff', 15 | alignItems: 'center', 16 | justifyContent: 'center', 17 | }, 18 | }); 19 | -------------------------------------------------------------------------------- /routes/homeStack.js: -------------------------------------------------------------------------------- 1 | import { createStackNavigator } from 'react-navigation-stack'; 2 | import { createAppContainer } from 'react-navigation' 3 | import Home from '../screens/Home'; 4 | import ContactsList from '../screens/ContactsList'; 5 | import ContactDetail from '../screens/ContactDetail'; 6 | 7 | const screens = { 8 | Contacts: { 9 | screen: Home 10 | }, 11 | ContactList: { 12 | screen: ContactsList 13 | }, 14 | Detail: { 15 | screen: ContactDetail 16 | } 17 | } 18 | 19 | const homeStack = createStackNavigator(screens) 20 | export default createAppContainer(homeStack) -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "expo": { 3 | "name": "contactapp", 4 | "slug": "contactapp", 5 | "version": "1.0.0", 6 | "orientation": "portrait", 7 | "icon": "./assets/icon.png", 8 | "splash": { 9 | "image": "./assets/splash.png", 10 | "resizeMode": "contain", 11 | "backgroundColor": "#ffffff" 12 | }, 13 | "updates": { 14 | "fallbackToCacheTimeout": 0 15 | }, 16 | "assetBundlePatterns": [ 17 | "**/*" 18 | ], 19 | "ios": { 20 | "supportsTablet": true 21 | }, 22 | "android": { 23 | "adaptiveIcon": { 24 | "foregroundImage": "./assets/adaptive-icon.png", 25 | "backgroundColor": "#FFFFFF" 26 | } 27 | }, 28 | "web": { 29 | "favicon": "./assets/favicon.png" 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /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-community/masked-view": "0.1.10", 12 | "@react-navigation/native": "^5.9.4", 13 | "expo": "~41.0.1", 14 | "expo-contacts": "~9.1.2", 15 | "expo-status-bar": "~1.0.4", 16 | "react": "16.13.1", 17 | "react-dom": "16.13.1", 18 | "react-native": "https://github.com/expo/react-native/archive/sdk-41.0.0.tar.gz", 19 | "react-native-elements": "^3.4.2", 20 | "react-native-gesture-handler": "~1.10.2", 21 | "react-native-reanimated": "~2.1.0", 22 | "react-native-safe-area-context": "3.2.0", 23 | "react-native-screens": "~3.0.0", 24 | "react-native-web": "~0.13.12", 25 | "react-navigation": "^4.4.4", 26 | "react-navigation-stack": "^2.10.4" 27 | }, 28 | "devDependencies": { 29 | "@babel/core": "^7.9.0" 30 | }, 31 | "private": true 32 | } 33 | -------------------------------------------------------------------------------- /screens/Home.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { TouchableWithoutFeedback } from 'react-native' 3 | import { View, Text, StyleSheet, Button } from 'react-native' 4 | 5 | export default function Home({ navigation }) { 6 | const navigationHandler = () => { 7 | navigation.navigate("ContactList") 8 | } 9 | return ( 10 | 11 | {/* */} 12 | 13 | To view your contacts please press the button bellow 14 | 15 | 16 | 17 | 18 | Contacts 19 | 20 | 21 | 22 | 23 | ) 24 | } 25 | 26 | const styles = StyleSheet.create({ 27 | container: { 28 | alignItems: "center", 29 | flex: 1, 30 | }, 31 | button: { 32 | padding: 20, 33 | borderRadius: 10, 34 | marginTop: "30%", 35 | backgroundColor: "#87CEEB", 36 | color: "white" 37 | }, 38 | heading: { 39 | fontSize: 18, 40 | marginTop: "30%" 41 | }, 42 | buttonText: { 43 | color: "white", 44 | fontWeight: "bold", 45 | fontSize: 16 46 | } 47 | }) -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | -------------------------------------------------------------------------------- /screens/ContactDetail.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { View, Text, StyleSheet, TextInput } from 'react-native' 3 | import { Avatar } from 'react-native-elements'; 4 | export default function ContactDetail({ navigation }) { 5 | return ( 6 | 7 | 8 | 9 | { 10 | navigation.getParam('image') !== "" ? 11 | : 18 | 23 | } 24 | 25 | 26 | 27 | {navigation.getParam('name')} 28 | 29 | 30 | 31 | Phone Number: 32 | 33 | 37 | 38 | 39 | ) 40 | } 41 | 42 | const styles = StyleSheet.create({ 43 | container: { 44 | // alignItems: "center" 45 | }, 46 | imageContainer: { 47 | flex: 1, 48 | alignItems: 'center', 49 | justifyContent: 'center', 50 | }, 51 | imageContainerWraper: { 52 | height: 150 53 | }, 54 | inputField: { 55 | fontWeight: "400", 56 | fontSize: 20, 57 | width: "60%", 58 | alignItems: "center", 59 | borderBottomColor: "black", 60 | borderBottomWidth: 1, 61 | paddingLeft: 30 62 | }, 63 | name: { 64 | alignItems: "center", 65 | height: 40, 66 | justifyContent: "center" 67 | }, 68 | nameText: { 69 | fontSize: 22, 70 | fontWeight: "bold" 71 | }, 72 | phno: { 73 | flexDirection: "row", 74 | marginTop: 20, 75 | height: 50 76 | 77 | }, 78 | heading: { 79 | fontSize: 16, 80 | fontWeight: "bold", 81 | marginTop: "4%", 82 | marginLeft: 10 83 | } 84 | }) -------------------------------------------------------------------------------- /screens/ContactsList.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react' 2 | import { View, Text, StyleSheet, FlatList, ActivityIndicator } from 'react-native' 3 | import * as Contacts from 'expo-contacts' 4 | import { Avatar } from 'react-native-elements'; 5 | import { TouchableOpacity } from 'react-native'; 6 | export default function ContactsList({ navigation }) { 7 | const [isLoading, setIsLoading] = useState(true) 8 | const [contacts, setContact] = useState([]) 9 | useEffect(() => { 10 | (async () => { 11 | const { status } = await Contacts.requestPermissionsAsync(); 12 | if (status === 'granted') { 13 | const { data } = await Contacts.getContactsAsync({ 14 | fields: [Contacts.Fields.Emails, Contacts.Fields.PhoneNumbers, Contacts.Fields.Image], 15 | }); 16 | if (data.length > 0) { 17 | setContact(data) 18 | setIsLoading(false) 19 | } 20 | } 21 | })(); 22 | }, []) 23 | 24 | const renderItems = ({ item }) => { 25 | return ( 26 | navigation.navigate("Detail", { 27 | name: item.name, 28 | phone: item.phoneNumbers[0].number, 29 | image: item.image ? item.image.uri : "" 30 | })}> 31 | 32 | 33 | { 34 | item.imageAvailable ? 35 | : 42 | 47 | } 48 | 49 | 50 | {item.name} 51 | {item?.phoneNumbers ? item.phoneNumbers[0].number : null} 52 | 53 | 54 | 55 | ) 56 | } 57 | const emptyComponent = () => { 58 | return ( 59 | 60 | No Contact Found 61 | 62 | ) 63 | } 64 | return ( 65 | 66 | 67 | { 68 | isLoading ? 69 | 70 | 71 | : null 72 | } 73 | 78 | 79 | 80 | ) 81 | } 82 | 83 | const styles = StyleSheet.create({ 84 | container: { 85 | flex: 1, 86 | }, 87 | searchField: { 88 | backgroundColor: "#d3d3d3", 89 | height: 50, 90 | fontSize: 36, 91 | paddingLeft: 10 92 | }, 93 | listSection: { 94 | flex: 1, 95 | marginLeft: 10 96 | }, 97 | contactNotFoundText: { 98 | color: "red" 99 | }, 100 | contactNotFoundView: { 101 | flex: 1, 102 | alignItems: "center", 103 | marginTop: 20 104 | }, 105 | renderContainer: { 106 | flex: 1, 107 | flexDirection: "row", 108 | marginBottom: 5, 109 | height: 60, 110 | alignItems: "center", 111 | borderBottomWidth: 1, 112 | borderColor: "#D4D4D4" 113 | }, 114 | contactName: { 115 | fontSize: 16, 116 | fontWeight: "bold", 117 | marginBottom: 5 118 | }, 119 | details: { 120 | marginLeft: 10 121 | } 122 | 123 | }) --------------------------------------------------------------------------------