Skip to content

Commit

Permalink
Google Vision API and Spoonacular API implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
mirzanorazman committed Nov 2, 2019
1 parent 648c223 commit cda5c8a
Show file tree
Hide file tree
Showing 9 changed files with 723 additions and 21 deletions.
6 changes: 5 additions & 1 deletion App.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@ import { createAppContainer } from "react-navigation";
import { createStackNavigator } from "react-navigation-stack";

import HomeScreen from "./src/screens/HomeScreen";
import RecipeListScreen from "./src/screens/RecipeListScreen";
import RecipeScreen from "./src/screens/RecipeScreen";

const MainNavigator = createStackNavigator(
{
Home: { screen: HomeScreen }
Home: { screen: HomeScreen },
RecipeList: { screen: RecipeListScreen },
Recipe: { screen: RecipeScreen }
},
{
initialRouteName: "Home"
Expand Down
5 changes: 5 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@
"react-native-vector-icons": "^6.6.0",
"react-native-web": "^0.11.7",
"react-navigation": "^4.0.10",
"react-navigation-stack": "^1.10.3"
"react-navigation-stack": "^1.10.3",
"expo-image-picker": "~7.0.0",
"expo-permissions": "~7.0.0"
},
"devDependencies": {
"babel-preset-expo": "^7.1.0"
Expand Down
6 changes: 4 additions & 2 deletions src/components/CustomText.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ class CustomText extends Component {

async componentDidMount() {
await Font.loadAsync({
RobotoRegular: require("../resources/fonts/Roboto-Regular.ttf")
RobotoRegular: require("../resources/fonts/Roboto-Regular.ttf"),
RobotoLight: require("../resources/fonts/Roboto-Light.ttf"),
RobotoBold: require("../resources/fonts/Roboto-Bold.ttf")
});

this.setState({
Expand All @@ -31,7 +33,7 @@ class CustomText extends Component {
const font = {
text: {
fontFamily: "RobotoRegular",
fontSize: 56
fontSize: 30
}
};

Expand Down
28 changes: 28 additions & 0 deletions src/data/mock_recipes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
const mock_recipes = [
{
id: 1,
title: "Shish Kabob Marinade",
img: "https://spoonacular.com/recipeImages/115764-312x231.jpg",
ready_in_minutes: 10
},
{
id: 2,
title: "Easy Apple Dumplings with Sprite",
img: "https://spoonacular.com/recipeImages/1036801-312x231.jpg",
ready_in_minutes: 15
},
{
id: 3,
title: "Simple Honey-Glazed Salmon",
img: "https://spoonacular.com/recipeImages/181272-312x231.jpg",
ready_in_minutes: 20
},
{
id: 4,
title: "Roasted Sweet Potato Wedges",
img: "https://spoonacular.com/recipeImages/303223-312x231.jpeg",
ready_in_minutes: 30
}
];

export default mock_recipes;
227 changes: 210 additions & 17 deletions src/screens/HomeScreen.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,29 @@
import React from "react";
import { View, StyleSheet, ScrollView, Dimensions } from "react-native";
import { ListItem, Button, Icon, SearchBar } from "react-native-elements";
import {
View,
StyleSheet,
ScrollView,
Dimensions,
ActivityIndicator
} from "react-native";
import {
ListItem,
Button,
Icon,
SearchBar,
Overlay,
Text
} from "react-native-elements";
import * as Permissions from "expo-permissions";
import Constants from "expo-constants";
import * as ImagePicker from "expo-image-picker";

import CustomText from "../components/CustomText";

import fridge_items from "../data/fridge_items";
import { ThemeColors } from "react-navigation";

import * as theme from "../theme";

const { width, height } = Dimensions.get("window");

Expand All @@ -14,12 +33,26 @@ class HomeScreen extends React.Component {
};

state = {
search: ""
search: "",
uploading: false,
image: null,
foodItems: fridge_items,
updatedState: [],
previousState: [],
isOverlayVisible: false,
isOverlayLoading: true
};

render() {
const { search } = this.state;
async componentDidMount() {
if (Constants.platform.ios) {
const { status } = await Permissions.askAsync(Permissions.CAMERA);
if (status !== "granted") {
alert("Sorry, we need camera roll permissions to make this work!");
}
}
}

render() {
return (
<View style={styles.container}>
<CustomText style={styles.headerText}>Your Fridge</CustomText>
Expand All @@ -30,7 +63,7 @@ class HomeScreen extends React.Component {
value={search}
/> */}
<ScrollView style={styles.listContainer}>
{fridge_items.map((l, i) => (
{/* {fridge_items.map((l, i) => (
<ListItem
key={i}
title={l.icon + " " + l.name}
Expand All @@ -41,34 +74,194 @@ class HomeScreen extends React.Component {
}}
bottomDivider
/>
))} */}
{this.state.foodItems.map((food, index) => (
<ListItem
key={food.id}
title={food.name}
badge={{
value: food.quantity,
textStyle: { color: "white" },
containerStyle: { marginTop: -10 }
}}
bottomDivider
/>
))}
{this.state.uploading && (
<ActivityIndicator size="large" color="#00ff00" />
)}
</ScrollView>
<View style={styles.buttonContainer}>
<Icon
name="add-a-photo"
reverse={true}
color="#FCAE6B"
onPress={() => console.log("hello")}
onPress={this.takePhoto}
/>
{/* <Button
icon={<Icon name="add-a-photo" size={17} color="white" />}
title=" Add item"
buttonStyle={styles.button}
/> */}
{/* <Button
icon={<Icon name="add-a-photo" size={17} color="white" />}
// title=" Add item"
buttonStyle={styles.button}
/> */}
<Button
icon={<Icon name="shuffle" color="white" />}
title=" Generate Recipe"
buttonStyle={styles.button}
onPress={() =>
this.props.navigation.navigate("RecipeList", {
foodItems: this.state.foodItems
})
}
/>
</View>

<Overlay isVisible={this.state.isOverlayVisible}>
<View>
<Text h2>Confirm items?</Text>
{this.state.isOverlayLoading && (
<ActivityIndicator size="large" color="#FCAE6B" />
)}
{this.state.updatedState.map(food => (
<ListItem key={food.id} title={food.name} bottomDivider />
))}
<Button
title="Yes"
onPress={() => {
this.setState({ updatedState: [] });
this.setState({ isOverlayVisible: false });
this.setState({ isOverlayLoading: true });
}}
/>
<Button
title="No"
onPress={() => {
this.setState({ foodItems: this.state.previousState });
this.setState({ isOverlayVisible: false });
this.setState({ updatedState: [] });
this.setState({ isOverlayLoading: true });
}}
/>
</View>
</Overlay>
</View>
);
}

takePhoto = async () => {
let pickerResult = await ImagePicker.launchCameraAsync({
allowsEditing: false,
base64: true
//aspect: [4, 3]
});
this.setState({ isOverlayVisible: true });
//console.log(pickerResult)
this.handleImagePicked(pickerResult);
};

handleImagePicked = async pickerResult => {
try {
this.setState({ uploading: true });

if (!pickerResult.cancelled) {
//this.setState({ image: pickerResult.uri }); // "data:image/jpeg;base64" +

this.submitToGoogle(pickerResult);
}
} catch (e) {
console.log(e);
alert("Upload failed, sorry :(");
}
};

submitToGoogle = async pickerResult => {
try {
let body = JSON.stringify({
requests: [
{
image: {
content: pickerResult.base64
},
features: [
{
maxResults: 10,
type: "OBJECT_LOCALIZATION"
}
]
}
]
});
let response = await fetch(
"https://vision.googleapis.com/v1/images:annotate?key=AIzaSyC870k5CTBVu-_bHAzTGILAMqiUg5V_p4A",
{
headers: {
Accept: "application/json",
"Content-Type": "application/json"
},
method: "POST",
body: body
}
);
let responseJson = await response.json();

let scannedFoods =
responseJson["responses"][0]["localizedObjectAnnotations"];

// preserve the previous state so that we can undo changes in the Overlay
this.setState({ previousState: this.state.foodItems });

this.setState({ isOverlayLoading: true });

// Iterate through all scanned items and add it to foodItems array
scannedFoods.forEach(food => {
// console.log(food["name"])

// create a copy of the foodItem array
let foodListToUpdate = this.state.foodItems.slice();

// check if food list already contains the scanned item
let foodToUpdate = foodListToUpdate.find(oldFood => {
return oldFood.name === food["name"];
});

// if foodList already contains the item, we increment its quantity
if (foodToUpdate) {
// Update its quantity
foodToUpdate["quantity"] += 1;

// keep track of updated state to show in overlay
let newFoodItem = {
id: Math.floor(Math.random() * Math.floor(100)), // return random no from 0 to 100
name: food["name"],
quantity: 1
};

this.setState({
updatedState: [...this.state.updatedState, newFoodItem]
});

// commit changes to the state
this.setState({
foodItems: foodListToUpdate
});
} else {
let newFoodItem = {
id: Math.floor(Math.random() * Math.floor(100)), // return random no from 0 to 100
name: food["name"],
quantity: 1
};

// keep track of updated state to show in overlay
this.setState({
updatedState: [...this.state.updatedState, newFoodItem]
});

this.setState({
foodItems: [...this.state.foodItems, newFoodItem]
});
}
});
this.setState({ uploading: false });
this.setState({ isOverlayLoading: false });
console.log(this.state.updatedState);
} catch (error) {
console.log(error);
}
};
}

const styles = StyleSheet.create({
Expand Down
Loading

0 comments on commit cda5c8a

Please sign in to comment.