Skip to content

Commit 43d5cc7

Browse files
authored
Merge pull request #19 from CodingFactory-Repos/feature/search
Feature/search
2 parents 5fcefd0 + 4247b3f commit 43d5cc7

File tree

6 files changed

+7254
-31
lines changed

6 files changed

+7254
-31
lines changed

src/components/navigators/TabBarNavigation/TabNavigator.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,14 @@ import {
88
TAB_BAR_NAVIGATOR_ROUTES,
99
TabBarNavigatorParamList,
1010
} from './TabNavigator.interfaces.ts';
11-
import {Imager} from '../../../screens/imager/Imager';
11+
import { Imager } from '../../../screens/imager/Imager';
12+
import { Search } from '../../../screens/search/Search.tsx';
1213
import CarrotNavigator from '../CarrotNavigator/CarrotNavigator.tsx';
1314
import {Image, Text, View} from 'react-native';
1415

1516
const Tab = createBottomTabNavigator<TabBarNavigatorParamList>();
1617

1718
const PlateScreen = () => null;
18-
const SearchScreen = () => null;
1919

2020
const TabNavigator = () => {
2121
const {navigate} = useNavigation();
@@ -98,7 +98,7 @@ const TabNavigator = () => {
9898
/>
9999
<Tab.Screen
100100
name={TAB_BAR_NAVIGATOR_ROUTES.SEARCH}
101-
component={SearchScreen}
101+
component={Search}
102102
options={{
103103
tabBarLabel: 'Search',
104104
tabBarIcon: ({color, size}) => (

src/screens/carrot/components/MockApiComponent.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ import {
88
} from 'react-native';
99
import {useNavigation} from '@react-navigation/native';
1010
import NutritionInfo from './NutritionInfo';
11-
import {CARROT_NAVIGATOR_ROUTES} from '../../../components/navigators/CarrotNavigator/CarrotNavigator.interfaces.ts';
11+
import { CARROT_NAVIGATOR_ROUTES } from '../../../components/navigators/CarrotNavigator/CarrotNavigator.interfaces.ts';
12+
import { useSelector } from 'react-redux';
1213

1314
const fakeApiData = {
1415
product_name: 'Ourson Chocolat',
@@ -23,8 +24,8 @@ const fakeApiData = {
2324
ecoscore_score: '75',
2425
nutriscore_grade: 'B',
2526
};
26-
2727
const App = () => {
28+
const {product} = useSelector(state => state.product);
2829
const {goBack} = useNavigation();
2930

3031
return (
@@ -33,7 +34,8 @@ const App = () => {
3334
<TouchableOpacity onPress={() => goBack()}>
3435
<Text>Go back</Text>
3536
</TouchableOpacity>
36-
<NutritionInfo data={fakeApiData} />
37+
{/* <NutritionInfo data={fakeApiData} /> */}
38+
<NutritionInfo data={product} />
3739
</ScrollView>
3840
</SafeAreaView>
3941
);

src/screens/imager/Imager.tsx

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,11 @@
11
import React from 'react';
22
import {StyleSheet, Text} from 'react-native';
33
import {SafeAreaView} from 'react-native-safe-area-context';
4-
import {
5-
Camera,
6-
useCameraDevice,
7-
useCameraFormat,
8-
useCameraPermission,
9-
useCodeScanner,
10-
} from 'react-native-vision-camera';
4+
import { Camera, useCameraDevice, useCameraFormat, useCameraPermission, useCodeScanner } from 'react-native-vision-camera';
115
import {useNavigation} from '@react-navigation/native';
126
import {useDispatch} from 'react-redux';
137

14-
import {getProduct} from '../../service/apiCall';
8+
import {getProductByBarcode} from '../../service/apiCall';
159
import {setProduct} from '../../service/redux/slices/productSlice';
1610
import {TAB_BAR_NAVIGATOR_ROUTES} from '../../components/navigators/TabBarNavigation/TabNavigator.interfaces.ts';
1711

@@ -28,7 +22,7 @@ export const Imager = () => {
2822
onCodeScanned: codes => {
2923
codes.forEach(code => {
3024
if (code.type === 'ean-13') {
31-
getProduct(code.value).then(result => {
25+
getProductByBarcode(code.value).then(result => {
3226
console.log(`Product found : ${result.name}`);
3327
dispatch(setProduct(result));
3428
navigation.navigate(TAB_BAR_NAVIGATOR_ROUTES.CARROT);

src/screens/search/Search.tsx

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
import React, { useState } from 'react';
2+
import { useNavigation } from '@react-navigation/native';
3+
import { Text, TextInput, SafeAreaView, StyleSheet, TouchableOpacity, View, ScrollView, Image } from 'react-native';
4+
import { getProductByName } from '../../service/apiCall';
5+
import { CARROT_NAVIGATOR_ROUTES } from '../../components/navigators/CarrotNavigator/CarrotNavigator.interfaces.ts';
6+
import { useDispatch } from 'react-redux';
7+
import { setProduct } from '../../service/redux/slices/productSlice.ts';
8+
9+
export const Search = () => {
10+
const [query, setQuery] = useState('');
11+
const [products, setProducts] = useState([]);
12+
const navigation = useNavigation();
13+
const dispatch = useDispatch();
14+
15+
async function searchForProduct(productName: string) {
16+
let products = await getProductByName(productName);
17+
setProducts(products);
18+
}
19+
20+
function goToDetail(product)
21+
{
22+
dispatch(setProduct(product));
23+
navigation.navigate(CARROT_NAVIGATOR_ROUTES.MOCK);
24+
}
25+
26+
const renderProducts = () => {
27+
return products.map((product, index) => (
28+
<TouchableOpacity key={index} style={styles.card} onPress={() => goToDetail(product)}>
29+
<View style={styles.header}>
30+
<Image style={styles.image} source={{ uri: product.image }} />
31+
<View style={styles.headerText}>
32+
<Text style={styles.title}>{product.name}</Text>
33+
<Text style={styles.subtitle}>{product.energy100g}kg</Text>
34+
</View>
35+
<Text style={styles.badge}>
36+
{product.nutriscore} - "{product.nutriscore_point}/100"
37+
</Text>
38+
</View>
39+
</TouchableOpacity>
40+
));
41+
};
42+
43+
return (
44+
<SafeAreaView style={styles.screen}>
45+
<TextInput
46+
placeholder='Search a product...'
47+
style={styles.input}
48+
onChangeText={text => {
49+
setQuery(text);
50+
if(text == '') setProducts([])
51+
}
52+
}
53+
/>
54+
<TouchableOpacity style={styles.button} onPress={() => {setProducts([]); searchForProduct(query);}}>
55+
<Text style={styles.buttonText}>Search</Text>
56+
</TouchableOpacity>
57+
<ScrollView>
58+
{renderProducts()}
59+
</ScrollView>
60+
</SafeAreaView>
61+
);
62+
};
63+
64+
const styles = StyleSheet.create({
65+
screen: {
66+
flex: 1,
67+
backgroundColor: 'white',
68+
},
69+
input: {
70+
borderRadius: 20,
71+
borderColor: '#E5E7EB',
72+
borderWidth: 1,
73+
margin: '5%',
74+
padding: 10,
75+
},
76+
button: {
77+
backgroundColor: '#4B5563',
78+
marginHorizontal: '30%',
79+
marginTop: 10,
80+
borderRadius: 20,
81+
alignItems: 'center',
82+
justifyContent: 'center',
83+
paddingVertical: 10,
84+
},
85+
buttonText: {
86+
color: 'white',
87+
fontWeight: 'bold',
88+
},
89+
card: {
90+
backgroundColor: '#FFFFFF',
91+
borderRadius: 8,
92+
margin: 8,
93+
padding: 16,
94+
shadowColor: '#000',
95+
shadowOffset: { width: 0, height: 2 },
96+
shadowOpacity: 0.23,
97+
shadowRadius: 2.62,
98+
elevation: 4,
99+
},
100+
header: {
101+
flexDirection: 'row',
102+
alignItems: 'center',
103+
justifyContent: 'space-between',
104+
},
105+
image: {
106+
width: 50,
107+
height: 50,
108+
borderRadius: 25,
109+
},
110+
headerText: {
111+
flex: 1,
112+
marginLeft: 8,
113+
},
114+
title: {
115+
fontWeight: 'bold',
116+
fontSize: 16,
117+
},
118+
subtitle: {
119+
color: '#4B5563',
120+
fontSize: 14,
121+
},
122+
badge: {
123+
backgroundColor: '#FECACA',
124+
color: '#B91C1C',
125+
borderRadius: 20,
126+
padding: 4,
127+
fontSize: 12,
128+
},
129+
});
130+
131+
export default Search;

src/service/apiCall.ts

Lines changed: 59 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,62 @@
1-
const apiUrl = 'https://world.openfoodfacts.org/api/v2/product/';
2-
3-
export async function getProduct(barcode: string) {
4-
const response = await fetch(apiUrl + barcode);
5-
const json = await response.json();
6-
7-
let data = {};
8-
data.code = json.code;
9-
data.name = json.product.product_name;
10-
data.nutriscore = json.product.nutriscore_grade;
11-
data.categories = json.product.categories;
12-
data.nutriments = json.product.nutriments;
13-
data.energy = json.product.nutriments.energy;
14-
data.energy100g = json.product.nutriments['energy-kcal_100g'];
15-
data.image = json.product.image_front_thumb_url;
16-
data.nutriscore_point = json.product.nutriscore_score;
1+
const apiUrlBarcode = 'https://world.openfoodfacts.org/api/v2/';
2+
const apiUrlSearch = 'https://world.openfoodfacts.org/cgi/search.pl?json=1&search_simple=1&search_terms='
3+
4+
export async function getProductByBarcode(barcode: string) {
5+
let data = {}, rawJson = {};
6+
await fetch(`${apiUrlBarcode}product/${barcode}`)
7+
.then(response => response.json())
8+
.then(json => rawJson = json);
9+
10+
data.code = rawJson.code;
11+
data.name = rawJson.product.product_name;
12+
data.nutriscore = rawJson.product.nutriscore_grade;
13+
data.categories = rawJson.product.categories;
14+
data.nutriments = rawJson.product.nutriments;
15+
data.energy = rawJson.product.nutriments.energy;
16+
data.energy100g = rawJson.product.nutriments['energy-kcal_100g'];
17+
data.image = rawJson.product.image_front_thumb_url;
18+
data.nutriscore_point = rawJson.product.nutriscore_score;
1719

1820
return data;
1921
}
22+
23+
export async function getProductByName(productName: string)
24+
{
25+
if ( productName.length == 0 ) return [];
26+
let data = [], rawJson = {};
27+
await fetch(`${apiUrlSearch}${encodeURIComponent(productName)}`)
28+
.then(response => response.json())
29+
.catch(err => console.error(err))
30+
.then(json => rawJson = json);
31+
32+
for(let i = 0; i <= 15; i++)
33+
{
34+
let object = {};
35+
if (!rawJson.products[i].product_name || rawJson.products[i].product_name == '') continue;
36+
37+
let alreadyPresent = false;
38+
// no foreach, because we want to use break;
39+
for(let j = 0; j < data.length; j++)
40+
{
41+
if(data[j].name.toLocaleLowerCase() == rawJson.products[i].product_name.toLocaleLowerCase())
42+
{
43+
alreadyPresent = true;
44+
break;
45+
}
46+
}
47+
48+
if(alreadyPresent) continue;
49+
50+
object.name = rawJson.products[i].product_name;
51+
object.nutriscore = rawJson.products[i].nutriscore_grade;
52+
object.categories = rawJson.products[i].categories;
53+
object.nutriments = rawJson.products[i].nutriments;
54+
object.energy = rawJson.products[i].nutriments.energy;
55+
object.energy100g = rawJson.products[i].nutriments['energy-kcal_100g'];
56+
object.image = rawJson.products[i].image_front_thumb_url;
57+
object.nutriscore_point = rawJson.products[i].nutriscore_score;
58+
data.push(object);
59+
}
60+
61+
return data;
62+
}

0 commit comments

Comments
 (0)