diff --git a/LeftOvers/Models/Profile.tsx b/LeftOvers/Models/Profile.tsx index 1dc1775..20bd71f 100644 --- a/LeftOvers/Models/Profile.tsx +++ b/LeftOvers/Models/Profile.tsx @@ -1,30 +1,41 @@ export default class Profile { - private _name: string; - private _avatar: string; - private _allergy: string[]; - private _diets: string[]; + public name: string; + public avatar: string; + public allergies: string[]; + public diets: string[]; + public isActive: string; + public isWaiting: string - constructor( name: string, avatar: string, allergy: string[], diets: string[]) { - this._name = name; - this._avatar = avatar; - this._allergy = allergy; - this._diets = diets; + constructor( name: string, avatar: string, allergies: string[], diets: string[], isActive: string, isWaiting: string) { + this.name = name; + this.avatar = avatar; + this.diets = diets; + this.allergies = allergies; + this.isActive = isActive; + this.isWaiting = isWaiting } - get name(): string { - return this._name; - } + // get name(): string { + // return this._name; + // } + // get avatar(): string{ + // return this._avatar; + // } - get avatar(): string{ - return this._avatar; - } + // get allergies(): string[]{ + // return this._allergies; + // } - get allergy(): string[]{ - return this._allergy; - } + // get diets(): string[]{ + // return this._diets; + // } - get diets(): string[]{ - return this._diets; - } + // get isActive(): string{ + // return this._isActive; + // } + + // get isWaiting(): string{ + // return this._isWaiting; + // } } \ No newline at end of file diff --git a/LeftOvers/Services/Ingredients/IIngredientService.tsx b/LeftOvers/Services/Ingredients/IIngredientService.tsx index 926f9be..687386c 100644 --- a/LeftOvers/Services/Ingredients/IIngredientService.tsx +++ b/LeftOvers/Services/Ingredients/IIngredientService.tsx @@ -5,4 +5,7 @@ export default interface IIngredientService { getIngredientById(id: number): Promise; getIngredientByLetter(id: string): Promise; getfilteredIngredient(prompt: string): Promise; + getAvailableIngredient(): Promise, + addIngredient(newIngredient: Ingredient): Promise, + delIngredient(idIngredient: number): Promise } \ No newline at end of file diff --git a/LeftOvers/Services/Ingredients/IngredientsServices.tsx b/LeftOvers/Services/Ingredients/IngredientsServices.tsx index d50cc89..eb2f5e4 100644 --- a/LeftOvers/Services/Ingredients/IngredientsServices.tsx +++ b/LeftOvers/Services/Ingredients/IngredientsServices.tsx @@ -1,6 +1,8 @@ +import AsyncStorage from "@react-native-async-storage/async-storage"; import Ingredient from "../../Models/Ingredient"; import IIngredientService from "./IIngredientService"; import axios from 'axios'; +import eventEmitter from "../../screens/EventEmitter"; export default class IngredientService implements IIngredientService { private readonly API_URL = "http://leftovers.alwaysdata.net/ingredients"; @@ -41,4 +43,36 @@ export default class IngredientService implements IIngredientService { throw new Error('Erreur lors de la récupération des ingrédients : ' + error.message); } } + + async getAvailableIngredient(): Promise { + const results = await AsyncStorage.getItem('ingredient') + const availableIngredient = JSON.parse(results) + if(availableIngredient.length == 0){ + availableIngredient.push(new Ingredient(-1, "None")) + } + console.log("AvailableIngredient:", availableIngredient) + return availableIngredient; + } + + async addIngredient(newIngredient: Ingredient): Promise { + let selectedIngredients = await this.getAvailableIngredient() + const exists = selectedIngredients.find((ingredient) => ingredient.id === newIngredient.id); + if (!exists) { + let existingAvailableIngredient = await AsyncStorage.getItem('ingredient'); + existingAvailableIngredient = existingAvailableIngredient ? JSON.parse(existingAvailableIngredient) : []; + const updatedAvailableIngredient = [...existingAvailableIngredient, newIngredient]; + await AsyncStorage.setItem('ingredient', JSON.stringify(updatedAvailableIngredient)); + eventEmitter.emit('ingredientAdded'); + return true + } + return false + } + + async delIngredient(idIngredient: number): Promise { + let selectedIngredients = await this.getAvailableIngredient() + const updatedIngredients = selectedIngredients.filter((ingredient) => ingredient.id !== idIngredient); + await AsyncStorage.setItem('ingredient', JSON.stringify(updatedIngredients)); + eventEmitter.emit('ingredientDeleted'); + return true + } } diff --git a/LeftOvers/Services/Profiles/IProfileService.ts b/LeftOvers/Services/Profiles/IProfileService.ts index 425a961..3e628d4 100644 --- a/LeftOvers/Services/Profiles/IProfileService.ts +++ b/LeftOvers/Services/Profiles/IProfileService.ts @@ -1,7 +1,7 @@ -import Profil from "../../Models/Profil"; +import Profile from "../../Models/Profile"; export default interface IProfileService { - getProfiles(): Promise, - addProfile(new_profile: Profil): Promise, - delProfile(profile_name_to_del: string): Promise + getProfiles(): Promise, + addProfile(newProfile: Profile): Promise, + delProfile(index: number): Promise } \ No newline at end of file diff --git a/LeftOvers/Services/Profiles/ProfileService.ts b/LeftOvers/Services/Profiles/ProfileService.ts index 30646bf..8704434 100644 --- a/LeftOvers/Services/Profiles/ProfileService.ts +++ b/LeftOvers/Services/Profiles/ProfileService.ts @@ -1,41 +1,32 @@ -import Profil from "../../Models/Profil"; +import Profile from "../../Models/Profile"; import IProfileService from "./IProfileService"; import AsyncStorage from "@react-native-async-storage/async-storage"; +import eventEmitter from "../../screens/EventEmitter"; export default class ProfileService implements IProfileService { - async getProfiles(): Promise { - const results = await AsyncStorage.getItem('profiles'); - const tmp = JSON.parse(results) - let existingProfiles: Profil[] = [] - for (let item of tmp) { - existingProfiles.push(new Profil(item._name, item._avatar, item._allergy, item._diets)) + async getProfiles(): Promise { + const results = await AsyncStorage.getItem('profiles') + const existingProfiles = JSON.parse(results) + if(existingProfiles.length == 0){ + existingProfiles.push(new Profile("None", "logo.png", [], [], "none", "none")) } return existingProfiles; } - async addProfile(new_profile : Profil): Promise { - const existingProfiles = await this.getProfiles() - for (let current_profile of existingProfiles) { - if (current_profile.name == new_profile.name) { - console.log("Tried to create a profil already existing !") - return false - } - } - await AsyncStorage.setItem('profiles', JSON.stringify([...existingProfiles, new_profile])) + async addProfile(newProfile : Profile): Promise { + let existingProfiles = await AsyncStorage.getItem('profiles') + existingProfiles = existingProfiles ? JSON.parse(existingProfiles) : []; + const updatedProfiles = [...existingProfiles, newProfile]; + await AsyncStorage.setItem('profiles', JSON.stringify(updatedProfiles)); + eventEmitter.emit("profileAdded") return true } - async delProfile(profile_name_to_del: string): Promise { - const existing_profiles = await this.getProfiles() - let key: number = -1 - for (let current_profile of existing_profiles) { - if (current_profile.name == profile_name_to_del) { - let updated_profile = existing_profiles.splice(key, 1) - await AsyncStorage.setItem('profiles', JSON.stringify(updated_profile)) - return true - } - key ++ - } - return false + async delProfile(index: number): Promise { + const existingProfiles = await this.getProfiles() + const updatedProfiles = existingProfiles.filter((profile, i) => i !== index); + await AsyncStorage.setItem('profiles', JSON.stringify(updatedProfiles)); + eventEmitter.emit('profileDeleted'); + return true } } \ No newline at end of file diff --git a/LeftOvers/components/ListWithoutSelect.tsx b/LeftOvers/components/ListWithoutSelect.tsx index a095f3c..1fa93e8 100644 --- a/LeftOvers/components/ListWithoutSelect.tsx +++ b/LeftOvers/components/ListWithoutSelect.tsx @@ -12,6 +12,11 @@ export default function ListWithoutSelect(props: ListProps) { const [selected, setSelected] = React.useState([]); const colors = useContext(ColorContext).colors; + let listContent = [] + props.content.forEach((val) => { + listContent.push({value: val, disabled: true}) + }) + const styles = StyleSheet.create({ titleBar: { flexDirection: "row", @@ -80,7 +85,7 @@ export default function ListWithoutSelect(props: ListProps) { return ( setSelected(val)} - data={props.content} + data={listContent} save="value" search={false} arrowicon={} @@ -97,5 +102,4 @@ export default function ListWithoutSelect(props: ListProps) { placeholder={props.title} label={props.title}/> ); -} - +} \ No newline at end of file diff --git a/LeftOvers/components/ProfileElement.tsx b/LeftOvers/components/ProfileElement.tsx index 2f4f0d9..63cc38d 100644 --- a/LeftOvers/components/ProfileElement.tsx +++ b/LeftOvers/components/ProfileElement.tsx @@ -1,4 +1,4 @@ -import React, { useContext, useState } from 'react'; +import React, { useContext } from 'react'; import {StyleSheet,Pressable, Text, View, Image} from 'react-native'; import ColorContext from '../theme/ColorContext'; @@ -6,35 +6,15 @@ type Profile = { name: string avatar: string isActive: string + isWaiting: string disableSelection: boolean + changeStatusWaiting: () => void + separatorDisplay: string } export default function ProfileElement(props : Profile) { const colors = useContext(ColorContext).colors - const [waiting, setWaiting] = useState("none") - const [separator, setSeparator] = useState("none") - const changeStatus = () => { - if (props.disableSelection){ - setWaiting("none") - } - else if (waiting == "flex"){ - setWaiting("none") - } - else{ - setWaiting("flex") - } - if (props.disableSelection){ - setSeparator("none") - } - else if (props.isActive == "flex" && waiting == "none"){ - setSeparator("flex") - } - else{ - setSeparator("none") - } - } - let imageSource if(props.avatar == ""){ imageSource=require("../assets/images/logo.png") @@ -102,7 +82,7 @@ export default function ProfileElement(props : Profile) { }); return ( - + @@ -112,8 +92,8 @@ export default function ProfileElement(props : Profile) { Activated - - + + Waiting... diff --git a/LeftOvers/components/ProfileSelection.tsx b/LeftOvers/components/ProfileSelection.tsx index daf409e..a6e6be9 100644 --- a/LeftOvers/components/ProfileSelection.tsx +++ b/LeftOvers/components/ProfileSelection.tsx @@ -10,35 +10,61 @@ import bracketRight from '../assets/images/angle_bracket_right.png'; type ProfileSelectionProps = { listProfile: Profile[] disableSelection: boolean + changeStatusWaiting: (number) => void } type Profile = { name: string avatar: string isActive: string + isWaiting: string } export default function ProfileSelection(props: ProfileSelectionProps) { const colors = useContext(ColorContext).colors; - const [cpt, setCpt] = useState(0); + const [separator, setSeparator] = useState("none") + const decreaseCounter = () => { + let index = props.listProfile.length - 1 if (cpt > 0) { setCpt(cpt - 1); + index = cpt - 1 } else { setCpt(props.listProfile.length - 1); } + changeSeparator(index) }; const increaseCounter = () => { + let index = 0 if (cpt < props.listProfile.length - 1) { setCpt(cpt + 1); + index = cpt+1 } else { setCpt(0); } + changeSeparator(index) }; + const changeSeparator = (index) => { + if (props.disableSelection){ + setSeparator("none") + } + else if (props.listProfile[index].isActive == "flex" && props.listProfile[index].isWaiting == "flex"){ + setSeparator("flex") + } + else{ + setSeparator("none") + } + } + + const changeStatus = () => { + props.changeStatusWaiting(cpt) + changeSeparator(cpt) + } + const styles = StyleSheet.create({ background: { width: "92%", @@ -58,7 +84,7 @@ export default function ProfileSelection(props: ProfileSelectionProps) { - + diff --git a/LeftOvers/components/RecipeElement.tsx b/LeftOvers/components/RecipeElement.tsx index dfea589..bf8ffcf 100644 --- a/LeftOvers/components/RecipeElement.tsx +++ b/LeftOvers/components/RecipeElement.tsx @@ -1,5 +1,5 @@ -import React, {useContext, useState} from 'react'; -import {StyleSheet, Pressable, Text, View, Image, ScrollView, ImageSourcePropType} from 'react-native'; +import React, {useContext} from 'react'; +import {StyleSheet, Pressable, Text, View, Image, ScrollView} from 'react-native'; import brochette from '../assets/images/brochette.png'; import Union_left from '../assets/images/Union_left.png'; import Union_right from '../assets/images/Union_right.png'; @@ -255,7 +255,7 @@ export default function RecipeElement(props: RecipeElementProps) { Description - + {props.recipe.description} @@ -263,7 +263,5 @@ export default function RecipeElement(props: RecipeElementProps) { {convertToHoursMinutes(props.recipe.time_to_cook)} - ); - - + ); } \ No newline at end of file diff --git a/LeftOvers/components/RecipeElementReduce.tsx b/LeftOvers/components/RecipeElementReduce.tsx index 9a51467..656c6a6 100644 --- a/LeftOvers/components/RecipeElementReduce.tsx +++ b/LeftOvers/components/RecipeElementReduce.tsx @@ -1,6 +1,5 @@ import React, {useContext} from 'react'; import {StyleSheet, Text, View, Image} from 'react-native'; -import brochette from '../assets/images/brochette.png'; import background from '../assets/images/Background.png'; import ColorContext from '../theme/ColorContext'; diff --git a/LeftOvers/navigation/BottomBar.tsx b/LeftOvers/navigation/BottomBar.tsx index a65c78d..ee552e3 100644 --- a/LeftOvers/navigation/BottomBar.tsx +++ b/LeftOvers/navigation/BottomBar.tsx @@ -40,7 +40,7 @@ export default function BottomBar({ state, descriptors, navigation }) { bottom: 0, right: 0, left: 0, - height: "8%", + height: 60, backgroundColor: theme === 'dark' ? "#3F3C42" : "transparent" }, BottomBarBlurContainer: { diff --git a/LeftOvers/navigation/ProfileStackScreen.tsx b/LeftOvers/navigation/ProfileStackScreen.tsx index d1ccaff..5f55471 100644 --- a/LeftOvers/navigation/ProfileStackScreen.tsx +++ b/LeftOvers/navigation/ProfileStackScreen.tsx @@ -37,7 +37,7 @@ export default function ProfilesStackScreen({ navigation }) { const _handleSearch = () => { console.log('Searching'); } - const _handleHeaderAdd = () => navigation.navigate('ProfileCreation', { name: String }); + const _handleHeaderAdd = () => navigation.navigate('ProfileCreation'); return ( diff --git a/LeftOvers/screens/CreateProfile.tsx b/LeftOvers/screens/CreateProfile.tsx index 72a37c7..2303a20 100644 --- a/LeftOvers/screens/CreateProfile.tsx +++ b/LeftOvers/screens/CreateProfile.tsx @@ -6,9 +6,8 @@ import ValidateButton from '../components/ValidateButton'; import ColorContext from '../theme/ColorContext'; import ListWithoutSelect from '../components/ListWithoutSelect'; import ListSelect from '../components/ListSelect'; -import EventEmitter from './EventEmitter'; import * as ImagePicker from 'expo-image-picker'; -import AsyncStorage from '@react-native-async-storage/async-storage'; +import ProfileService from '../Services/Profiles/ProfileService'; export default function CreateProfile(props) { @@ -19,6 +18,7 @@ export default function CreateProfile(props) { const [avatar, setAvatar] = useState(''); const [selectedDiets, setSelectedDiets] = useState([]); const [selectedAllergies] = useState([]) + const profileService = new ProfileService() const handleSelectedDiets = (selectedValues) => { setSelectedDiets(selectedValues); @@ -32,9 +32,6 @@ export default function CreateProfile(props) { aspect: [4, 3], quality: 1, }); - - console.log(result); - if (!result.canceled) { setAvatar(result.assets[0].uri); } @@ -42,7 +39,7 @@ export default function CreateProfile(props) { let imageSource - if (props.avatar == ""){ + if (avatar == ""){ imageSource = require("../assets/images/logo.png") } else{ @@ -51,22 +48,15 @@ export default function CreateProfile(props) { const handleCreateProfile = async () => { try { - // Ton code pour récupérer les profils existants et ajouter un nouveau profil const newProfile = { name: name, avatar: avatar, diets: selectedDiets, allergies: all, isActive: "flex", + isWaiting: "none", }; - - // Mettre à jour AsyncStorage avec le nouveau profil - let existingProfiles = await AsyncStorage.getItem('profiles'); - existingProfiles = existingProfiles ? JSON.parse(existingProfiles) : []; - const updatedProfiles = [...existingProfiles, newProfile]; - await AsyncStorage.setItem('profiles', JSON.stringify(updatedProfiles)); - EventEmitter.emit('profileAdded'); - console.log('Profil créé :', newProfile); + profileService.addProfile(newProfile) props.navigation.goBack(); } catch (error) { console.error('Erreur lors de la création du profil :', error); diff --git a/LeftOvers/screens/FiltersSelection.tsx b/LeftOvers/screens/FiltersSelection.tsx index bc43c42..34a5092 100644 --- a/LeftOvers/screens/FiltersSelection.tsx +++ b/LeftOvers/screens/FiltersSelection.tsx @@ -1,46 +1,178 @@ -import React, {useContext} from 'react'; +import React, {useContext, useState, useEffect} from 'react'; import {StyleSheet, View, Text, ScrollView, useWindowDimensions} from 'react-native'; import {LinearGradient} from 'expo-linear-gradient'; import {SafeAreaProvider} from 'react-native-safe-area-context'; - import ValidateButton from '../components/ValidateButton'; import ListSelect from '../components/ListSelect'; import ListWithoutSelect from '../components/ListWithoutSelect'; import ProfileSelection from '../components/ProfileSelection'; import ColorContext from '../theme/ColorContext'; +import eventEmitter from './EventEmitter'; +import AsyncStorage from '@react-native-async-storage/async-storage'; +import ProfileService from '../Services/Profiles/ProfileService'; export default function FiltersSelection(props) { const {colors} = useContext(ColorContext); - const profiles = [ - {name: "Johnny Silverhand", avatar: "plus_small.png", isActive: "flex"}, - {name: "Panam Palmer", avatar: "plus_small.png", isActive: "none"}, - {name: "Goro Takemura", avatar: "plus_small.png", isActive: "none"}, - {name: "David Martinez", avatar: "plus_small.png", isActive: "flex"}, + const profilesHand = [ + {name: "None", avatar: "logo.png", diets: [], allergies: [], isActive: "none", isWaiting: "none"}, ] + const die = [{value: "Dairy free"}, {value: "Gluten free"}, {value: "Porkless"}, {value: "Vegan"}, {value: "Vegetarian"}, {value: "Pescatarian"}] + const profileService = new ProfileService() + const [profiles, setProfiles] = useState(profilesHand); + const [dieProfiles, setDieProfiles] = useState([]) + const [allProfiles, setAllProfiles] = useState([]) + const [dieAdd, setDieAdd] = useState([]) + const [allAdd, setAllAdd] = useState([]) + const [selectedDiets, setSelectedDiets] = useState([]) - let cptActive = 0 - profiles.forEach(function (value) { - if(value.isActive=="flex"){ - cptActive=cptActive+1 - } + const fetchProfiles = async () => { + setProfiles(await profileService.getProfiles()) + } + + const subscriptionAddProfile = eventEmitter.addListener('profileAdded', async () => { + fetchProfiles() + subscriptionAddProfile.remove() + eventEmitter.removeAllListeners('profileAdded') + eventEmitter.removeAllListeners('updateDietsAllergies') + eventEmitter.removeAllListeners('selectedProfilesUpdated') + }); + + const subscriptionUpdateDietsAllergies = eventEmitter.addListener('updateDietsAllergies', async() => { + setDieAdd(die.filter(isInProfileDiets)) + setAllAdd([]) + subscriptionUpdateDietsAllergies.remove() + subscriptionUpdateProfiles.remove(); + eventEmitter.removeAllListeners('updateDietsAllergies') }) - const die = [{value: "Dairy free"}, {value: "Gluten free"}, {value: "Porkless"}, {value: "Vegan"}, {value: "Vegetarian"}, {value: "Pescatarian"}] + useEffect(() => { + fetchProfiles() + }, []); - const allProfiles = [{value: "Skimmed Milk"}, {value: "Nuts"}] - const dieProfiles = [{value: "Porkless"}, {value: "Pescatarian"}] + async function handleSaveSelectedProfiles(){ + try { + profiles.forEach((val) => { + if(val.isWaiting == "flex"){ + if(val.isActive == "none"){ + val.isActive = "flex" + } + else{ + val.isActive = "none" + } + } + val.isWaiting = "none" + }) + await AsyncStorage.setItem('profiles', JSON.stringify(profiles)); + fetchProfiles() + eventEmitter.emit("selectedProfilesUpdated") + } catch (error) { + console.error('Error occured when updating active profiles:', error, selectedDiets); + } + }; + + const subscriptionUpdateProfiles = eventEmitter.addListener('selectedProfilesUpdated', async () => { + updateDiets() + updateAllergies() + eventEmitter.emit("updateDietsAllergies") + subscriptionUpdateProfiles.remove(); + eventEmitter.removeAllListeners('profileAdded') + eventEmitter.removeAllListeners('updateDietsAllergies') + eventEmitter.removeAllListeners('selectedProfilesUpdated') + }); - function isInProfileDiets(element, index, array) { - let retType = true - dieProfiles.forEach(function (diets) { - if(diets.value==element.value){ - retType = false + const updateDiets = () => { + let dieTemp = [] + let retType = true + profiles.forEach((profile) => { + if(profile.isActive == "flex"){ + profile.diets.forEach((diet) => { + retType = true + dieTemp.forEach((val) => { + if(val == diet){ + retType = false } - }) - return retType + }) + if(retType){ + dieTemp.push(diet) + } + }) + } + }) + setDieProfiles(dieTemp) + } + + const updateAllergies = () => { + let allTemp = [] + let retType = true + profiles.forEach((profile) => { + if(profile.isActive == "flex"){ + profile.allergies.forEach((allergy) => { + retType = true + allTemp.forEach((val) => { + if(val == allergy){ + retType = false + } + }) + if(retType){ + allTemp.push(allergy) + } + }) + } + }) + setAllProfiles(allTemp) + } + + const changeStatusWaiting = (cpt) => { + if(profiles[cpt].isWaiting == "none"){ + profiles[cpt].isWaiting = "flex" + } + else{ + profiles[cpt].isWaiting = "none" + } + handleSaveWaiting() + } + + const handleSaveWaiting = async () => { + try { + await AsyncStorage.setItem('profiles', JSON.stringify(profiles)); + fetchProfiles(); + } catch (error) { + console.error('Error occured when updating waiting profiles:', error); + } + }; + + let cptActive = 0 + const updateCptActive = () => { + cptActive = 0 + profiles.forEach(function (value) { + if(value.isActive=="flex"){ + cptActive=cptActive+1 } - const dieAdd = die.filter(isInProfileDiets); - const allAdd = [] + })} + let cptWaiting = 0 + const updateCptWaiting = () => { + cptWaiting = 0 + profiles.forEach(function (value) { + if(value.isWaiting=="flex"){ + cptWaiting=cptWaiting+1 + } + })} + updateCptActive() + updateCptWaiting() + + function isInProfileDiets(element) { + let retType = true + dieProfiles.forEach((diet) => { + if(diet==element.value){ + retType = false + } + }) + return retType + } + + const handleSelectedDiets = (selectedValues) => { + setSelectedDiets(selectedValues); + }; const styles = StyleSheet.create({ container: { @@ -112,12 +244,12 @@ export default function FiltersSelection(props) { Profiles - {cptActive} selected, 1 waiting + {cptActive} selected, {cptWaiting} waiting - + - console.log("change selected profile")}> + @@ -134,9 +266,9 @@ export default function FiltersSelection(props) { Additional Filters {dieAdd.length} available - + - + props.navigation.navigate("IngredientSelection")}> diff --git a/LeftOvers/screens/HomePage.tsx b/LeftOvers/screens/HomePage.tsx index 697dd82..7fed9f9 100644 --- a/LeftOvers/screens/HomePage.tsx +++ b/LeftOvers/screens/HomePage.tsx @@ -2,58 +2,42 @@ import React, { useContext, useState, useEffect } from 'react'; import { StyleSheet, View, Text, Pressable, Image, ScrollView } from 'react-native'; import {LinearGradient} from 'expo-linear-gradient'; import {SafeAreaProvider} from 'react-native-safe-area-context'; - import ValidateButton from '../components/ValidateButton'; import ProfileSelection from '../components/ProfileSelection'; import FoodElementText from '../components/FoodElementText'; import ColorContext from '../theme/ColorContext'; - import bracketLeft from '../assets/images/angle_bracket_left.png'; import bracketRight from '../assets/images/angle_bracket_right.png'; - import AsyncStorage from '@react-native-async-storage/async-storage'; -import EventEmitter from './EventEmitter'; +import eventEmitter from './EventEmitter'; +import ProfileService from '../Services/Profiles/ProfileService'; export default function HomePage({ navigation, props }) { const colors = useContext(ColorContext).colors const profilesHand = [ - {name: "None", avatar: "logo.png", isActive: "none"} + {name: "None", avatar: "logo.png", isActive: "none", isWaiting: "none"} ] const ingredientListHand = [{name: "None"}] const [profiles, setProfiles] = useState(profilesHand); const [ingredientList, setIngredientList] = useState(ingredientListHand) - const handleGetProfiles = async () => { - try { - const existingProfiles = await AsyncStorage.getItem('profiles'); - return JSON.parse(existingProfiles) || []; - } catch (error) { - console.log(error); - return []; - } - } + const profileService = new ProfileService() const handleGetAvailableIngredient = async () => { try { const existingAvailableIngredient = await AsyncStorage.getItem('ingredient'); return JSON.parse(existingAvailableIngredient) || []; } catch (error) { - console.log(error); + console.error(error); return []; } } const fetchProfiles = async () => { - const existingProfiles = await handleGetProfiles(); - if (existingProfiles.length != 0){ - setProfiles(existingProfiles); - } - else{ - setProfiles(profilesHand) - } - }; + setProfiles(await profileService.getProfiles()) + } const fetchAvailableIngredient = async () => { const existingAvailableIngredient = await handleGetAvailableIngredient(); @@ -65,37 +49,71 @@ export default function HomePage({ navigation, props }) { } }; - const subscriptionAddProfile = EventEmitter.addListener('profileAdded', async () => { + const subscriptionAddProfile = eventEmitter.addListener('profileAdded', async () => { fetchProfiles(); + subscriptionAddProfile.remove(); + eventEmitter.removeAllListeners('profileAdded') + eventEmitter.removeAllListeners('profileDeleted') + eventEmitter.removeAllListeners('ingredientAdded') + eventEmitter.removeAllListeners('ingredientDeleted') + eventEmitter.removeAllListeners('selectedProfilesUpdated') }); - const subscriptionDeleteProfile = EventEmitter.addListener('profileDeleted', async () => { + const subscriptionDeleteProfile = eventEmitter.addListener('profileDeleted', async () => { if (profiles.length == 1){ setProfiles(profilesHand) } else{ fetchProfiles(); } + subscriptionDeleteProfile.remove(); + eventEmitter.removeAllListeners('profileAdded') + eventEmitter.removeAllListeners('profileDeleted') + eventEmitter.removeAllListeners('ingredientAdded') + eventEmitter.removeAllListeners('ingredientDeleted') + eventEmitter.removeAllListeners('selectedProfilesUpdated') }); - const subscriptionAddIngredient = EventEmitter.addListener('ingredientAdded', async () => { + const subscriptionAddIngredient = eventEmitter.addListener('ingredientAdded', async () => { fetchAvailableIngredient(); + subscriptionAddIngredient.remove(); + eventEmitter.removeAllListeners('profileAdded') + eventEmitter.removeAllListeners('profileDeleted') + eventEmitter.removeAllListeners('ingredientAdded') + eventEmitter.removeAllListeners('ingredientDeleted') + eventEmitter.removeAllListeners('selectedProfilesUpdated') }); - const subscriptionDeleteIngredient = EventEmitter.addListener('ingredientDeleted', async () => { + const subscriptionDeleteIngredient = eventEmitter.addListener('ingredientDeleted', async () => { if (ingredientList.length == 1){ setIngredientList(ingredientListHand) } else{ fetchAvailableIngredient(); } + subscriptionDeleteIngredient.remove(); + eventEmitter.removeAllListeners('profileAdded') + eventEmitter.removeAllListeners('profileDeleted') + eventEmitter.removeAllListeners('ingredientAdded') + eventEmitter.removeAllListeners('ingredientDeleted') + eventEmitter.removeAllListeners('selectedProfilesUpdated') + }); + + const subscriptionUpdateSelectedProfile = eventEmitter.addListener('selectedProfilesUpdated', async () => { + fetchProfiles(); + subscriptionUpdateSelectedProfile.remove(); + eventEmitter.removeAllListeners('profileAdded') + eventEmitter.removeAllListeners('profileDeleted') + eventEmitter.removeAllListeners('ingredientAdded') + eventEmitter.removeAllListeners('ingredientDeleted') + eventEmitter.removeAllListeners('selectedProfilesUpdated') }); useEffect(() => { //AsyncStorage.clear() fetchProfiles(); if(profiles.length == 0){ - setProfiles([{name: "None", avatar: "plus_small.png", isActive: "none"}]) + setProfiles(profilesHand) } fetchAvailableIngredient(); }, []); @@ -229,7 +247,7 @@ export default function HomePage({ navigation, props }) { {nbActiveProfiles()} selected - + val += 1}/> navigation.navigate('FiltersSelection')}/> @@ -256,7 +274,7 @@ export default function HomePage({ navigation, props }) { navigation.navigate("IngredientSelection")}/> - navigation.navigate("RecipeSuggestion")}/> + navigation.navigate('RecipeSuggestion', {ingredients: ingredientList})}/> diff --git a/LeftOvers/screens/IngredientSelection.tsx b/LeftOvers/screens/IngredientSelection.tsx index 1bd07a7..ec589a2 100644 --- a/LeftOvers/screens/IngredientSelection.tsx +++ b/LeftOvers/screens/IngredientSelection.tsx @@ -9,7 +9,7 @@ import IngredientService from '../Services/Ingredients/IngredientsServices'; import ColorContext from '../theme/ColorContext'; import ValidateButton from '../components/ValidateButton'; import AsyncStorage from '@react-native-async-storage/async-storage'; -import EventEmitter from './EventEmitter'; +import eventEmitter from './EventEmitter'; import plus from '../assets/images/plus.png'; import moins from '../assets/images/minus.png'; @@ -37,7 +37,7 @@ export default function IngredientSelection(props) { setResponse(filtered); } } catch (error) { - console.log(error); + console.error(error); } finally { setIsLoading(false); } @@ -50,10 +50,9 @@ export default function IngredientSelection(props) { const loadIngredients = async () => { try { - const ingredients = await ingredientService.getAllIngredient(); - setResponse(ingredients); + setResponse(await ingredientService.getAllIngredient()); } catch (error) { - console.log(error); + console.error(error); } finally { setIsLoading(false); } @@ -80,7 +79,9 @@ const loadIngredients = async () => { const ChooseItem = React.memo(({ value }: { value: Ingredient }) => ( <> - + RemoveIngredient(value.id)}> + + RemoveIngredient(value.id)}> @@ -94,7 +95,7 @@ const loadIngredients = async () => { const existingAvailableIngredient = await AsyncStorage.getItem('ingredient'); return JSON.parse(existingAvailableIngredient) || []; } catch (error) { - console.log(error); + console.error(error); return []; } } @@ -105,7 +106,7 @@ const fetchAvailableIngredient = async () => { setSelectedIngredients(existingAvailableIngredient); } else{ - setSelectedIngredients([{value: "None"}]) + setSelectedIngredients([new Ingredient(-1, "None")]) } }; @@ -117,13 +118,14 @@ const fetchAvailableIngredient = async () => { existingAvailableIngredient = existingAvailableIngredient ? JSON.parse(existingAvailableIngredient) : []; const updatedAvailableIngredient = [...existingAvailableIngredient, newIngredient]; await AsyncStorage.setItem('ingredient', JSON.stringify(updatedAvailableIngredient)); - EventEmitter.emit('ingredientAdded'); + eventEmitter.emit('ingredientAdded'); + fetchAvailableIngredient(); console.log('Ingredient Added:', newIngredient); ChangeAvailableSize(false) } } catch(error){ - console.log("Error occured during the addition of Ingredient:", error) + console.error("Error occured during the addition of Ingredient:", error) } }; @@ -131,28 +133,16 @@ const fetchAvailableIngredient = async () => { try{ const updatedIngredients = selectedIngredients.filter((ingredient) => ingredient.id !== idIngredient); await AsyncStorage.setItem('ingredient', JSON.stringify(updatedIngredients)); - EventEmitter.emit('ingredientDeleted'); + eventEmitter.emit('ingredientDeleted'); fetchAvailableIngredient(); setSelectedIngredients(updatedIngredients); ChangeAvailableSize(true) } catch (error){ - console.log("Error occured during the suppression of Ingredient:", error) + console.error("Error occured during the suppression of Ingredient:", error) } }; - const subscriptionAddIngredient = EventEmitter.addListener('ingredientAdded', async () => { - fetchAvailableIngredient(); - }); - const subscriptionDeleteIngredient = EventEmitter.addListener('ingredientDeleted', async () => { - if (selectedIngredients.length == 1){ - setSelectedIngredients([{title: "None"}]) - } - else{ - fetchAvailableIngredient(); - } - }); - const ChangeAvailableSize = (remove: boolean) => { if(remove){ if (selectedIngredients.length == 1){ @@ -176,7 +166,7 @@ const fetchAvailableIngredient = async () => { setAvailableSize(90) } else if (selectedIngredients.length == 1){ - if(selectedIngredients[0].value == "None"){ + if(selectedIngredients[0].name == "None"){ setAvailableSize(90) } else{ @@ -197,7 +187,7 @@ const fetchAvailableIngredient = async () => { const ingredientsByLetter = await ingredientService.getIngredientByLetter(letter); setResponse(ingredientsByLetter); } catch (error) { - console.log(error); + console.error(error); } finally { setIsLoading(false); } diff --git a/LeftOvers/screens/ModifyProfile.tsx b/LeftOvers/screens/ModifyProfile.tsx index f243f3f..771c502 100644 --- a/LeftOvers/screens/ModifyProfile.tsx +++ b/LeftOvers/screens/ModifyProfile.tsx @@ -15,27 +15,23 @@ export default function ModifyProfile(props) { const [profile, setProfile] = useState(null); const route = useRoute(); - - - const handleGetProfileByName = async (profileName) => { try { const existingProfiles = await AsyncStorage.getItem('profiles'); const profiles = JSON.parse(existingProfiles) || []; const matchedProfile = profiles.find(profile => profile.name === profileName); - console.log("Le profil choisit : " + matchedProfile); return matchedProfile || null; } catch (error) { - console.log("Erreur lors de la récupération du profil :", error); + console.error("Erreur lors de la récupération du profil :", error); return null; } }; const fetchProfiles = async () => { - const selectedProfil = await handleGetProfileByName(route.params); - setProfile(selectedProfil); -}; + const selectedProfil = await handleGetProfileByName(route.params); + setProfile(selectedProfil); + }; useEffect(() => { fetchProfiles(); @@ -48,7 +44,7 @@ useEffect(() => { - (console.log("Profile Modified"))}> + console.log("")}> diff --git a/LeftOvers/screens/Profiles.tsx b/LeftOvers/screens/Profiles.tsx index e763b8f..b572319 100644 --- a/LeftOvers/screens/Profiles.tsx +++ b/LeftOvers/screens/Profiles.tsx @@ -3,23 +3,21 @@ import { StyleSheet, View, Modal, Pressable, Text, Image, ScrollView, useWindowD import { LinearGradient } from 'expo-linear-gradient'; import { SafeAreaProvider } from 'react-native-safe-area-context'; - +import ProfileService from '../Services/Profiles/ProfileService'; import ProfileDetails from '../components/ProfileDetails'; import ColorContext from '../theme/ColorContext'; -import AsyncStorage from '@react-native-async-storage/async-storage'; -import EventEmitter from './EventEmitter'; +import eventEmitter from './EventEmitter'; import { PaperProvider, Portal } from 'react-native-paper'; export default function Profiles({navigation, props}) { const colors = useContext(ColorContext).colors - + const profileService = new ProfileService() const [visible, setVisible] = useState(false); const [profiles, setProfiles] = useState([]); const [selectedProfileIndex, setSelectedProfileIndex] = useState(null); const goDetails = (name: string) => navigation.navigate('ProfileCreation', name); - const raisePopUp = (index) => { setSelectedProfileIndex(index) setVisible(true) @@ -30,10 +28,7 @@ export default function Profiles({navigation, props}) { const handleDeleteProfile = async (index) => { try { - const updatedProfiles = profiles.filter((profile, i) => i !== index); - await AsyncStorage.setItem('profiles', JSON.stringify(updatedProfiles)); - EventEmitter.emit('profileDeleted'); - fetchProfiles(); + profileService.delProfile(index) setSelectedProfileIndex(index); erasePopUp(); } catch (error) { @@ -41,23 +36,22 @@ export default function Profiles({navigation, props}) { } }; - const handleGetProfiles = async () => { - try { - const existingProfiles = await AsyncStorage.getItem('profiles'); - return JSON.parse(existingProfiles) || []; - } catch (error) { - console.log("Error occured during GetProfiles", error); - return []; - } + const fetchProfiles = async () => { + setProfiles(await profileService.getProfiles()) } - const fetchProfiles = async () => { - const existingProfiles = await handleGetProfiles(); - setProfiles(existingProfiles); - }; + const subscription = eventEmitter.addListener('profileAdded', async () => { + fetchProfiles(); + subscription.remove(); + eventEmitter.removeAllListeners('profileAdded') + eventEmitter.removeAllListeners('profileDeleted') + }); - const subscription = EventEmitter.addListener('profileAdded', async () => { + const subscriptionDeletedProfile = eventEmitter.addListener('profileDeleted', async () => { fetchProfiles(); + subscriptionDeletedProfile.remove(); + eventEmitter.removeAllListeners('profileAdded') + eventEmitter.removeAllListeners('profileDeleted') }); useEffect(() => { diff --git a/LeftOvers/screens/RecipeDetails.tsx b/LeftOvers/screens/RecipeDetails.tsx index f11dcc7..553584c 100644 --- a/LeftOvers/screens/RecipeDetails.tsx +++ b/LeftOvers/screens/RecipeDetails.tsx @@ -102,7 +102,6 @@ export default function RecipeDetails({ route }) { return categories[0]; } - console.log("LA LISTE DES CATEGORY : " + categories) let bestMatch = { category: '', similarity: 0 }; for (const [name, categoriesList] of Object.entries(categoryMappings)) { @@ -176,7 +175,7 @@ export default function RecipeDetails({ route }) { const recipe = await recipesService.getRecipeById(recipeId); setResponse(recipe); } catch (error) { - console.log(error); + console.error(error); } finally{ setIsLoading(false) } diff --git a/LeftOvers/screens/RecipeSuggestion.tsx b/LeftOvers/screens/RecipeSuggestion.tsx index 537ec20..02a0149 100644 --- a/LeftOvers/screens/RecipeSuggestion.tsx +++ b/LeftOvers/screens/RecipeSuggestion.tsx @@ -30,7 +30,7 @@ export default function RecipeSuggestion({ route, navigation }) { const [colorFilters, setColorFilters] = useState(colors.cardDetail); const [selectedRecipes, setSelectedRecipes] = useState([]); const recipeService = new RecipesServices(); - const { ingredients } = route.params; + const {ingredients} = route.params; const limitedList = ingredients.slice(minCpt, maxCpt); let selectedIngredients: string[]; @@ -99,7 +99,7 @@ export default function RecipeSuggestion({ route, navigation }) { } } catch (error) { - console.log(error) + console.error(error) } }; @@ -114,7 +114,7 @@ export default function RecipeSuggestion({ route, navigation }) { //padding: "2%", paddingTop: 0, alignItems: "center", - justifyContent: "center" + justifyContent: "center", }, background: { @@ -153,7 +153,7 @@ export default function RecipeSuggestion({ route, navigation }) { height: "10%", width: "100%", flexDirection: 'row', - justifyContent: 'space-between', + justifyContent: 'space-around', alignItems: 'center', }, @@ -163,6 +163,37 @@ export default function RecipeSuggestion({ route, navigation }) { alignItems: "flex-start", justifyContent: "center", }, + + noRecipeView: { + width: "90%", + padding: "5%", + borderRadius: 15, + borderWidth: 1, + borderColor: colors.blocBorder, + backgroundColor: colors.buttonBackground, + alignItems: "center", + justifyContent: "center", + height: "40%", + marginLeft: 12, + }, + textNoRecipe: { + color: colors.cardElementBorder, + fontSize: 20, + textAlign: "center", + }, + smallText: { + color: colors.cardDetail, + fontSize: 15, + alignItems: "center", + justifyContent: "center", + }, + horizontalAlignmentNoRecipe: { + width: "100%", + flexDirection: 'row', + justifyContent: 'space-evenly', + alignItems: 'center', + flex: 0.9, + }, }); @@ -185,11 +216,22 @@ export default function RecipeSuggestion({ route, navigation }) { - + + Scroll/Swipe to see more Recipes + + + {Array.isArray(selectedRecipes) && selectedRecipes.length === 0 ? ( - No recipes + + No recipes found with those ingredients: + + {ingredients.length > 0 && ingredients.map((source, index) => ( + - {source.name} - + ))} + + ) : ( - selectedRecipes.map((recipe, index) => ( + selectedRecipes.map((recipe) => (