├── .expo-shared
└── assets.json
├── .gitignore
├── App.js
├── app.json
├── assets
├── adaptive-icon.png
├── favicon.png
├── icon.png
└── splash.png
├── babel.config.js
├── package-lock.json
├── package.json
└── src
├── api
└── yelp.js
├── assets
└── images
│ ├── burger.png
│ ├── cake.png
│ ├── pasta.png
│ ├── pizza.png
│ ├── smoothies.png
│ └── steak.png
├── components
├── Categories.js
├── Header.js
├── ItemCategory.js
├── RestaurantItem.js
├── Restaurants.js
└── SearchInput.js
├── hooks
└── useRestaurants.js
└── screens
├── HomeScreen.js
└── RestaurantScreen.js
/.expo-shared/assets.json:
--------------------------------------------------------------------------------
1 | {
2 | "12bb71342c6255bbf50437ec8f4441c083f47cdb74bd89160c15e4f43e52a1cb": true,
3 | "40b842e832070c58deac6aa9e08fa459302ee3f9da492c7e77d93d2fbf4a56fd": true
4 | }
5 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | .expo/
3 | dist/
4 | npm-debug.*
5 | *.jks
6 | *.p8
7 | *.p12
8 | *.key
9 | *.mobileprovision
10 | *.orig.*
11 | web-build/
12 |
13 | # macOS
14 | .DS_Store
15 |
--------------------------------------------------------------------------------
/App.js:
--------------------------------------------------------------------------------
1 | import { createAppContainer } from "react-navigation";
2 | import { createStackNavigator } from "react-navigation-stack";
3 | import HomeScreen from "./src/screens/HomeScreen";
4 | import RestaurantScreen from "./src/screens/RestaurantScreen";
5 |
6 | const navigator = createStackNavigator(
7 | {
8 | Home: HomeScreen,
9 | Restaurant: RestaurantScreen,
10 | },
11 | {
12 | initialRouteName: "Home",
13 | defaultNavigationOptions: {
14 | title: "BusinessSearch",
15 | },
16 | }
17 | );
18 |
19 | export default createAppContainer(navigator);
20 |
--------------------------------------------------------------------------------
/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "expo": {
3 | "name": "restaurants",
4 | "slug": "restaurants",
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 |
--------------------------------------------------------------------------------
/assets/adaptive-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harblaith7/React-Native-Crash-Course/dbf1ff8253916fecd030fb6c7a550f1760fe6104/assets/adaptive-icon.png
--------------------------------------------------------------------------------
/assets/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harblaith7/React-Native-Crash-Course/dbf1ff8253916fecd030fb6c7a550f1760fe6104/assets/favicon.png
--------------------------------------------------------------------------------
/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harblaith7/React-Native-Crash-Course/dbf1ff8253916fecd030fb6c7a550f1760fe6104/assets/icon.png
--------------------------------------------------------------------------------
/assets/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harblaith7/React-Native-Crash-Course/dbf1ff8253916fecd030fb6c7a550f1760fe6104/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 | "name": "restaurants",
3 | "version": "1.0.0",
4 | "main": "node_modules/expo/AppEntry.js",
5 | "scripts": {
6 | "start": "expo start",
7 | "android": "expo start --android",
8 | "ios": "expo start --ios",
9 | "web": "expo start --web",
10 | "eject": "expo eject"
11 | },
12 | "dependencies": {
13 | "axios": "^0.26.0",
14 | "expo": "~44.0.0",
15 | "expo-status-bar": "~1.2.0",
16 | "react": "17.0.1",
17 | "react-dom": "17.0.1",
18 | "react-native": "0.64.3",
19 | "react-native-web": "0.17.1",
20 | "react-navigation": "^4.4.4",
21 | "react-navigation-stack": "^2.10.4"
22 | },
23 | "devDependencies": {
24 | "@babel/core": "^7.12.9"
25 | },
26 | "private": true
27 | }
28 |
--------------------------------------------------------------------------------
/src/api/yelp.js:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 |
3 | export default axios.create({
4 | baseURL: "https://api.yelp.com/v3/businesses",
5 | headers: {
6 | Authorization: `Bearer fuefE5XzFBOYQrWACxEa1NzsOuUyCd9iKXGlotUC-9H4l5WHkveHLsd5yk60F7KGX1Mj9hKZ4M7eZYlMg0UfxdtYpTexTmCHPvGlCfPtKbHgThE90LtoQYyRfIMbYnYx`,
7 | },
8 | });
9 |
--------------------------------------------------------------------------------
/src/assets/images/burger.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harblaith7/React-Native-Crash-Course/dbf1ff8253916fecd030fb6c7a550f1760fe6104/src/assets/images/burger.png
--------------------------------------------------------------------------------
/src/assets/images/cake.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harblaith7/React-Native-Crash-Course/dbf1ff8253916fecd030fb6c7a550f1760fe6104/src/assets/images/cake.png
--------------------------------------------------------------------------------
/src/assets/images/pasta.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harblaith7/React-Native-Crash-Course/dbf1ff8253916fecd030fb6c7a550f1760fe6104/src/assets/images/pasta.png
--------------------------------------------------------------------------------
/src/assets/images/pizza.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harblaith7/React-Native-Crash-Course/dbf1ff8253916fecd030fb6c7a550f1760fe6104/src/assets/images/pizza.png
--------------------------------------------------------------------------------
/src/assets/images/smoothies.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harblaith7/React-Native-Crash-Course/dbf1ff8253916fecd030fb6c7a550f1760fe6104/src/assets/images/smoothies.png
--------------------------------------------------------------------------------
/src/assets/images/steak.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/harblaith7/React-Native-Crash-Course/dbf1ff8253916fecd030fb6c7a550f1760fe6104/src/assets/images/steak.png
--------------------------------------------------------------------------------
/src/components/Categories.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { FlatList, View } from "react-native";
3 | import ItemCategory from "./ItemCategory";
4 |
5 | export default function Categories({ commonCategories, setTerm, term }) {
6 | return (
7 |
8 | category.name}
11 | renderItem={({ item, index }) => (
12 | setTerm(item.name)}
17 | />
18 | )}
19 | horizontal
20 | showsHorizontalScrollIndicator={false}
21 | />
22 |
23 | );
24 | }
25 |
--------------------------------------------------------------------------------
/src/components/Header.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { StyleSheet, Text, View } from "react-native";
3 |
4 | export default function Header({ upperText, lowerText }) {
5 | return (
6 |
7 | {upperText}
8 | {lowerText}
9 |
10 | );
11 | }
12 |
13 | const styles = StyleSheet.create({
14 | header1: {
15 | fontSize: 35,
16 | marginTop: 60,
17 | marginHorizontal: 25,
18 | },
19 | header2: {
20 | fontSize: 40,
21 | marginHorizontal: 25,
22 | fontWeight: "bold",
23 | },
24 | });
25 |
--------------------------------------------------------------------------------
/src/components/ItemCategory.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Image, StyleSheet, Text, TouchableOpacity, View } from "react-native";
3 |
4 | export default function ItemCategory({
5 | index,
6 | category,
7 | active,
8 | onPressCategoryItem,
9 | }) {
10 | return (
11 |
12 |
21 |
22 |
23 |
24 | {category.name}
25 |
26 |
27 | );
28 | }
29 |
30 | const styles = StyleSheet.create({
31 | container: {
32 | width: 70,
33 | height: 100,
34 | borderRadius: 500,
35 | alignItems: "center",
36 | justifyContent: "center",
37 | shadowOffset: { width: 5, height: 5 },
38 | shadowColor: "black",
39 | shadowOpacity: 0.1,
40 | elevation: 3,
41 | marginBottom: 7,
42 | },
43 | image: {
44 | width: 35,
45 | height: 35,
46 | },
47 | imageContainer: {
48 | width: 50,
49 | height: 50,
50 | backgroundColor: "white",
51 | alignItems: "center",
52 | justifyContent: "center",
53 | borderRadius: 500,
54 | marginBottom: 5,
55 | },
56 | header: {
57 | fontWeight: "bold",
58 | },
59 | });
60 |
--------------------------------------------------------------------------------
/src/components/RestaurantItem.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Image, StyleSheet, Text, View } from "react-native";
3 |
4 | export default function RestaurantItem({ restaurant }) {
5 | return (
6 |
7 |
8 |
9 | {restaurant.name}
10 |
11 | {restaurant.rating} stars
12 | {restaurant.price}
13 |
14 |
15 |
16 | );
17 | }
18 |
19 | const styles = StyleSheet.create({
20 | container: {
21 | alignSelf: "stretch",
22 | height: 100,
23 | borderRadius: 500,
24 | shadowOffset: { width: 1, height: 3 },
25 | shadowColor: "black",
26 | shadowOpacity: 0.1,
27 | elevation: 3,
28 | backgroundColor: "white",
29 | marginVertical: 10,
30 | flexDirection: "row",
31 | alignItems: "center",
32 | },
33 | image: {
34 | width: 75,
35 | height: 75,
36 | borderRadius: 400,
37 | marginLeft: 10,
38 | },
39 | infoContainer: {
40 | flex: 1,
41 | paddingHorizontal: 10,
42 | },
43 | header: {
44 | fontSize: 18,
45 | fontWeight: "bold",
46 | marginBottom: 4,
47 | },
48 | info: {
49 | fontWeight: "bold",
50 | flexDirection: "row",
51 | },
52 | rating: {
53 | marginRight: 20,
54 | },
55 | money: {
56 | color: "gold",
57 | },
58 | });
59 |
--------------------------------------------------------------------------------
/src/components/Restaurants.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import {
3 | StyleSheet,
4 | View,
5 | Text,
6 | ActivityIndicator,
7 | FlatList,
8 | TouchableOpacity,
9 | } from "react-native";
10 | import { withNavigation } from "react-navigation";
11 | import RestaurantItem from "./RestaurantItem";
12 |
13 | function Restaurants({ data, error, loading, navigation }) {
14 | if (loading) return ;
15 |
16 | return (
17 |
18 | Top Restaurants
19 |
20 | restaurant.id}
23 | renderItem={({ item, index }) => (
24 | navigation.navigate("Restaurant", { id: item.id })}
26 | >
27 |
28 |
29 | )}
30 | />
31 |
32 | );
33 | }
34 |
35 | const styles = StyleSheet.create({
36 | container: {
37 | marginHorizontal: 25,
38 | marginVertical: 15,
39 | flex: 1,
40 | },
41 | header: {
42 | fontWeight: "bold",
43 | fontSize: 20,
44 | paddingBottom: 10,
45 | },
46 | });
47 |
48 | export default withNavigation(Restaurants);
49 |
--------------------------------------------------------------------------------
/src/components/SearchInput.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import { TextInput, View, StyleSheet } from "react-native";
3 | import { FontAwesome } from "@expo/vector-icons";
4 |
5 | export default function SearchInput({ setTerm }) {
6 | const [input, setInput] = useState("");
7 |
8 | return (
9 |
10 |
11 | setInput(text)}
16 | onEndEditing={() => {
17 | if (input) setTerm(input);
18 | setInput("");
19 | }}
20 | />
21 |
22 | );
23 | }
24 |
25 | const styles = StyleSheet.create({
26 | inputContainer: {
27 | marginTop: 5,
28 | marginHorizontal: 25,
29 | backgroundColor: "white",
30 | padding: 15,
31 | borderRadius: 40,
32 | flexDirection: "row",
33 | },
34 | elevation: {
35 | shadowOffset: { width: 5, height: 5 },
36 | shadowColor: "black",
37 | shadowOpacity: 0.1,
38 | elevation: 3,
39 | },
40 | input: {
41 | fontSize: 20,
42 | marginLeft: 10,
43 | },
44 | });
45 |
--------------------------------------------------------------------------------
/src/hooks/useRestaurants.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from "react";
2 | import yelp from "../api/yelp";
3 |
4 | export default (initialTerm = "Burger") => {
5 | const [results, setResults] = useState({
6 | data: null,
7 | loading: false,
8 | error: null,
9 | });
10 |
11 | const searchRestaurants = async (searchTerm) => {
12 | setResults({
13 | data: null,
14 | loading: true,
15 | error: null,
16 | });
17 | try {
18 | const response = await yelp.get("/search", {
19 | params: {
20 | limit: 15,
21 | term: searchTerm,
22 | location: "Ottawa",
23 | },
24 | });
25 | setResults({
26 | data: response.data.businesses,
27 | loading: false,
28 | error: null,
29 | });
30 | } catch (error) {
31 | setResults({
32 | data: null,
33 | loading: false,
34 | error: "Something went wrong",
35 | });
36 | }
37 | };
38 |
39 | return [results, searchRestaurants];
40 | };
41 |
--------------------------------------------------------------------------------
/src/screens/HomeScreen.js:
--------------------------------------------------------------------------------
1 | import { StatusBar } from "expo-status-bar";
2 | import { StyleSheet, View } from "react-native";
3 |
4 | import { useEffect, useState } from "react";
5 | import useRestaurants from "../hooks/useRestaurants";
6 | import Header from "../components/Header";
7 | import SearchInput from "../components/SearchInput";
8 | import Categories from "../components/Categories";
9 | import Restaurants from "../components/Restaurants";
10 |
11 | export default function HomeScreen() {
12 | const [term, setTerm] = useState("Burger");
13 | const [commonCategories] = useState([
14 | {
15 | name: "Burger",
16 | img: require("../assets/images/burger.png"),
17 | },
18 | {
19 | name: "Pizza",
20 | img: require("../assets/images/pizza.png"),
21 | },
22 | {
23 | name: "Dessert",
24 | img: require("../assets/images/cake.png"),
25 | },
26 | {
27 | name: "Drinks",
28 | img: require("../assets/images/smoothies.png"),
29 | },
30 | {
31 | name: "Steak",
32 | img: require("../assets/images/steak.png"),
33 | },
34 | {
35 | name: "Pasta",
36 | img: require("../assets/images/pasta.png"),
37 | },
38 | ]);
39 |
40 | const [{ data, loading, error }, searchResaurants] = useRestaurants(term);
41 |
42 | useEffect(() => {
43 | searchResaurants(term);
44 | }, [term]);
45 |
46 | return (
47 |
48 |
49 |
50 |
55 |
56 |
57 |
58 | );
59 | }
60 |
61 | const styles = StyleSheet.create({
62 | container: {
63 | flex: 1,
64 | backgroundColor: "rgb(253,253,253)",
65 | },
66 | header1: {
67 | fontSize: 35,
68 | marginTop: 60,
69 | marginHorizontal: 25,
70 | },
71 | header2: {
72 | fontSize: 40,
73 | marginHorizontal: 25,
74 | fontWeight: "bold",
75 | },
76 | });
77 |
--------------------------------------------------------------------------------
/src/screens/RestaurantScreen.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from "react";
2 | import {
3 | Dimensions,
4 | FlatList,
5 | Image,
6 | StyleSheet,
7 | Text,
8 | View,
9 | } from "react-native";
10 | import yelp from "../api/yelp";
11 |
12 | function RestaurantScreen({ navigation }) {
13 | const id = navigation.getParam("id");
14 | const [results, setResults] = useState({
15 | data: null,
16 | loading: false,
17 | error: null,
18 | });
19 |
20 | useEffect(() => {
21 | fetchRestaurant();
22 | }, []);
23 |
24 | const dimensions = Dimensions.get("window");
25 | const imageHeight = Math.round((dimensions.width * 9) / 16);
26 | const imageWidth = dimensions.width;
27 |
28 | const fetchRestaurant = async () => {
29 | setResults({
30 | data: null,
31 | loading: true,
32 | error: null,
33 | });
34 | try {
35 | const response = await yelp.get(`/${id}`);
36 |
37 | setResults({
38 | data: response.data,
39 | loading: false,
40 | error: null,
41 | });
42 | } catch (error) {
43 | setResults({
44 | data: null,
45 | loading: false,
46 | error: "something went wrong",
47 | });
48 | }
49 | };
50 |
51 | if (results.loading)
52 | return (
53 |
54 | Inside loading
55 |
56 | );
57 |
58 | if (results.error)
59 | return (
60 |
61 | Inside error
62 |
63 | );
64 |
65 | return (
66 |
67 | {results.data && (
68 | photo}
71 | renderItem={({ item }) => {
72 | console.log({ item });
73 | return (
74 |
78 | );
79 | }}
80 | />
81 | )}
82 |
83 | );
84 | }
85 |
86 | const styles = StyleSheet.create({
87 | image: {
88 | flex: 1,
89 | },
90 | });
91 |
92 | export default RestaurantScreen;
93 |
--------------------------------------------------------------------------------