diff --git a/LeftOvers/Models/Profil.tsx b/LeftOvers/Models/Profil.tsx index bbc12f5..a588dd2 100644 --- a/LeftOvers/Models/Profil.tsx +++ b/LeftOvers/Models/Profil.tsx @@ -1,20 +1,23 @@ export default class Profil { - private _id: number; private _name: string; + private _avatar: string; private _allergy: string[]; private _diets: string[]; - constructor(id: number, name: string) { - this._id = id; + constructor( name: string, avatar: string, allergy: string[], diets: string[]) { this._name = name; + this._avatar = avatar; + this._allergy = allergy; + this._diets = diets; } get name(): string { return this._name; } - get id(): number{ - return this._id; + + get avatar(): string{ + return this._avatar; } get allergy(): string[]{ diff --git a/LeftOvers/components/FoodElementText.tsx b/LeftOvers/components/FoodElementText.tsx index 68b1135..006abc8 100644 --- a/LeftOvers/components/FoodElementText.tsx +++ b/LeftOvers/components/FoodElementText.tsx @@ -6,6 +6,8 @@ import ColorContext from '../theme/ColorContext'; interface FoodElementTextProps { title : string + mainColour: string + secondaryColour: string } const componentHeight = 60; @@ -21,7 +23,7 @@ export default function FoodElementText(props : any) { justifyContent: 'center', width: "80%", borderRadius: 5, - backgroundColor: colors.carrouselBackground, + backgroundColor: props.mainColour ? props.mainColour : colors.ingredientBackground, marginHorizontal: "3%", }, text: { @@ -42,7 +44,7 @@ export default function FoodElementText(props : any) { height: 50, borderRadius: 5, borderWidth: 2, - borderColor: colors.cardDetail, + borderColor: props.secondaryColour ? props.secondaryColour : colors.foodElementBorder, flexDirection: 'row', justifyContent: 'space-between', }, diff --git a/LeftOvers/components/ListSelect.tsx b/LeftOvers/components/ListSelect.tsx index 12c1f93..202818f 100644 --- a/LeftOvers/components/ListSelect.tsx +++ b/LeftOvers/components/ListSelect.tsx @@ -6,10 +6,12 @@ import ColorContext from '../theme/ColorContext'; type ListProps = { title: string content : string[] + val : string[] + setSelected: any; } export default function ListSelect(props: ListProps) { - const [selected, setSelected] = React.useState([]); + const {colors} = useContext(ColorContext); const styles = StyleSheet.create({ @@ -83,7 +85,7 @@ export default function ListSelect(props: ListProps) { return ( setSelected(val)} + setSelected={(val) => props.setSelected(val)} data={props.content} save="value" search={false} diff --git a/LeftOvers/components/ListWithoutSelect.tsx b/LeftOvers/components/ListWithoutSelect.tsx index ce43197..3885f1b 100644 --- a/LeftOvers/components/ListWithoutSelect.tsx +++ b/LeftOvers/components/ListWithoutSelect.tsx @@ -5,7 +5,7 @@ import ColorContext from '../theme/ColorContext'; type ListProps = { title: string - content: {title: string}[] + content: {value: string}[] } export default function ListWithoutSelect(props: ListProps) { diff --git a/LeftOvers/components/ProfileDelete.tsx b/LeftOvers/components/ProfileDelete.tsx index 0afa801..5295cf2 100644 --- a/LeftOvers/components/ProfileDelete.tsx +++ b/LeftOvers/components/ProfileDelete.tsx @@ -6,8 +6,8 @@ import ColorContext from '../theme/ColorContext'; type ProfileProps = { name: string avatar: string - diets: {title: string}[] - allergies: {title: string}[] + diets: {value: string}[] + allergies: {value: string}[] } export default function ProfileDelete(props: ProfileProps) { @@ -84,7 +84,7 @@ export default function ProfileDelete(props: ProfileProps) { }, filters: { fontSize: 20, - color: colors.cardElementBackground, + color: colors.cardElementBorder, flex: 1, padding: "2%", paddingLeft: 0, diff --git a/LeftOvers/package-lock.json b/LeftOvers/package-lock.json index e324758..225e6b3 100644 --- a/LeftOvers/package-lock.json +++ b/LeftOvers/package-lock.json @@ -17,6 +17,7 @@ "axios": "^1.6.2", "expo": "~49.0.15", "expo-blur": "^12.4.1", + "expo-image-picker": "~14.3.2", "expo-linear-gradient": "~12.3.0", "expo-splash-screen": "~0.20.5", "expo-status-bar": "~1.6.0", @@ -9282,6 +9283,25 @@ "expo": "*" } }, + "node_modules/expo-image-loader": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/expo-image-loader/-/expo-image-loader-4.3.0.tgz", + "integrity": "sha512-2kqJIO+oYM8J3GbvTUHLqTSpt1dLpOn/X0eB4U4RTuzz/faj8l/TyQELsMBLlGAkweNUuG9LqznbaBz+WuSFEw==", + "peerDependencies": { + "expo": "*" + } + }, + "node_modules/expo-image-picker": { + "version": "14.3.2", + "resolved": "https://registry.npmjs.org/expo-image-picker/-/expo-image-picker-14.3.2.tgz", + "integrity": "sha512-xr/YeQMIYheXecWP033F2SPwpBlBR5xVCx7YSfSCTH8Y9pw7Z886agqKGbS9QBVGlzJ5qecJktZ6ASSzeslDVg==", + "dependencies": { + "expo-image-loader": "~4.3.0" + }, + "peerDependencies": { + "expo": "*" + } + }, "node_modules/expo-keep-awake": { "version": "12.3.0", "resolved": "https://registry.npmjs.org/expo-keep-awake/-/expo-keep-awake-12.3.0.tgz", @@ -24641,6 +24661,20 @@ "fontfaceobserver": "^2.1.0" } }, + "expo-image-loader": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/expo-image-loader/-/expo-image-loader-4.3.0.tgz", + "integrity": "sha512-2kqJIO+oYM8J3GbvTUHLqTSpt1dLpOn/X0eB4U4RTuzz/faj8l/TyQELsMBLlGAkweNUuG9LqznbaBz+WuSFEw==", + "requires": {} + }, + "expo-image-picker": { + "version": "14.3.2", + "resolved": "https://registry.npmjs.org/expo-image-picker/-/expo-image-picker-14.3.2.tgz", + "integrity": "sha512-xr/YeQMIYheXecWP033F2SPwpBlBR5xVCx7YSfSCTH8Y9pw7Z886agqKGbS9QBVGlzJ5qecJktZ6ASSzeslDVg==", + "requires": { + "expo-image-loader": "~4.3.0" + } + }, "expo-keep-awake": { "version": "12.3.0", "resolved": "https://registry.npmjs.org/expo-keep-awake/-/expo-keep-awake-12.3.0.tgz", diff --git a/LeftOvers/package.json b/LeftOvers/package.json index b148b0a..32e65bd 100644 --- a/LeftOvers/package.json +++ b/LeftOvers/package.json @@ -31,7 +31,8 @@ "react-native-splash-screen": "^3.3.0", "react-native-virtualized-view": "^1.0.0", "react-native-web": "~0.19.6", - "typescript": "^5.1.3" + "typescript": "^5.1.3", + "expo-image-picker": "~14.3.2" }, "devDependencies": { "@babel/core": "^7.20.0" diff --git a/LeftOvers/screens/CreateProfile.tsx b/LeftOvers/screens/CreateProfile.tsx index db3c9a5..06e41aa 100644 --- a/LeftOvers/screens/CreateProfile.tsx +++ b/LeftOvers/screens/CreateProfile.tsx @@ -1,32 +1,81 @@ -import React, { useContext } from 'react'; -import {StyleSheet, View, ScrollView, useWindowDimensions} from 'react-native'; +import React, { useContext, useState } from 'react'; +import {StyleSheet, View, ScrollView, useWindowDimensions, TextInput, Image, Text, NativeEventEmitter, Pressable} from 'react-native'; import { LinearGradient } from 'expo-linear-gradient'; import { SafeAreaProvider } from 'react-native-safe-area-context'; - -import ProfileModification from '../components/ProfileModification'; import ValidateButton from '../components/ValidateButton'; import ColorContext from '../theme/ColorContext'; +import ListWithoutSelect from '../components/ListWithoutSelect'; +import ListSelect from '../components/ListSelect'; +import AsyncStorage from '@react-native-async-storage/async-storage'; +import EventEmitter from './EventEmitter'; +import * as ImagePicker from 'expo-image-picker'; + export default function CreateProfile(props) { const { colors } = useContext(ColorContext) const all = [] const die = [{value: "Dairy free"}, {value: "Gluten free"}, {value: "Porkless"}, {value: "Vegan"}, {value: "Vegetarian"}, {value: "Pescatarian"}] + const [name, onChangeName] = useState(); + const [avatar, setAvatar] = useState(''); + const [selectedDiets, setSelectedDiets] = useState([]); + + const handleSelectedDiets = (selectedValues) => { + setSelectedDiets(selectedValues); + }; + + const pickImage = async () => { + // No permissions request is necessary for launching the image library + let result = await ImagePicker.launchImageLibraryAsync({ + mediaTypes: ImagePicker.MediaTypeOptions.All, + allowsEditing: true, + aspect: [4, 3], + quality: 1, + }); + + console.log(result); + + if (!result.canceled) { + setAvatar(result.assets[0].uri); + } + }; + + + let imageSource + if (props.avatar == "plus.png"){ + imageSource = require('../assets/images/plus.png') + } + else if (props.avatar == "plus_small.png"){ + imageSource = require('../assets/images/plus_small.png') + } + else{ + imageSource = require('../assets/images/logo.png') + } - const handleCreateProfile = () => { - const profileData = { - name: "Nom du profil", // Remplacez par le nom du profil - avatar: "Lien de l'avatar", // Remplacez par le lien de l'avatar - diets: die.map(item => item.value), // Liste des régimes - allergies: all, // Liste des allergies - }; + 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, + }; - localStorage.setItem('profile', JSON.stringify(profileData)); - console.log("Profil créé :", profileData); + // 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'); - // Redirection vers la page précédente avec un message de confirmation - props.navigation.goBack(); - // Affichage d'un message - alert("Profil créé !"); + console.log('Profil créé :', newProfile); + + props.navigation.goBack(); + + alert('Profil créé !'); + } catch (error) { + console.error('Erreur lors de la création du profil :', error); + } }; const styles = StyleSheet.create({ @@ -36,7 +85,74 @@ export default function CreateProfile(props) { flex: 1, padding: "2%", paddingTop: 0, + },background: { + flexDirection: 'column', + alignItems: 'center', + justifyContent: 'center', + borderRadius: 15, + backgroundColor: colors.cardBackground, + padding: "3%", + marginHorizontal: "3%", + borderWidth: 1, + borderColor: colors.blocBorder, + }, + + pseudoBar: { + flexDirection: "row", + alignItems: "center", + justifyContent: "center", + width: "100%", + marginHorizontal: "3%", + marginBottom: "3%", + }, + avatar: { + padding: "5%", + resizeMode: 'contain', + borderWidth: 2, + borderColor: colors.cardElementBorder, + borderRadius: 45, + height: "100%", + flex: 0.04, + }, + textInput: { + fontSize: 15, + color: colors.cardTitle, + borderRadius: 10, + borderWidth: 2, + borderStyle: 'dashed', + borderColor: colors.cardElementBorder, + alignItems: 'center', + textAlign: 'left', + flex: 0.8, + marginLeft: "7%", + padding: "2%", + }, + modify: { + height: "100%", + tintColor: colors.cardElementBorder, + resizeMode: 'contain', + flex: 0.1, + marginLeft: "3%", + }, + + filterBar: { + flexDirection: "row", + width: "85%", + paddingTop: "3%", + paddingBottom: "2%", + alignItems: "flex-end", + justifyContent: "center", + }, + filters: { + fontSize: 20, + color: colors.cardElementBorder, + flex: 1, }, + nbSelected: { + fontSize: 11, + color: colors.cardDetail, + textAlign: "right", + } }); return ( @@ -44,7 +160,22 @@ export default function CreateProfile(props) { - + + + + + + + + + Filters + 0 diets selected + + + + + + diff --git a/LeftOvers/screens/EventEmitter.tsx b/LeftOvers/screens/EventEmitter.tsx new file mode 100644 index 0000000..5ed089b --- /dev/null +++ b/LeftOvers/screens/EventEmitter.tsx @@ -0,0 +1,5 @@ +import { NativeEventEmitter } from 'react-native'; + +const eventEmitter = new NativeEventEmitter(); + +export default eventEmitter; \ No newline at end of file diff --git a/LeftOvers/screens/HomePage.tsx b/LeftOvers/screens/HomePage.tsx index c991b0c..e985087 100644 --- a/LeftOvers/screens/HomePage.tsx +++ b/LeftOvers/screens/HomePage.tsx @@ -167,7 +167,7 @@ export default function HomePage({ navigation, props }) { - + diff --git a/LeftOvers/screens/IngredientSelection.tsx b/LeftOvers/screens/IngredientSelection.tsx index 0691f8d..622aee8 100644 --- a/LeftOvers/screens/IngredientSelection.tsx +++ b/LeftOvers/screens/IngredientSelection.tsx @@ -19,7 +19,9 @@ export default function IngredientSelection(props) { const [selectedIngredients, setSelectedIngredients] = useState([]); const ingredientService = new IngredientService(); const {colors} = useContext(ColorContext); - + const [availableSize, setAvailableSize] = useState(0); + const [listVisibility, setListVisibility] = useState("none"); + const [availableVisibility, setAvailableVisibility] = useState("none"); const [searchQuery, setSearchQuery] = useState(''); const filterIngredients = async (query) => { @@ -63,10 +65,10 @@ const loadIngredients = async () => { SelectIngredient(value)}> - + - + ); @@ -75,26 +77,65 @@ const loadIngredients = async () => { RemoveIngredient(value.id)}> - + - + ); const SelectIngredient = (newIngredient: Ingredient) => { const exists = selectedIngredients.find((ingredient) => ingredient.id === newIngredient.id); - + console.log("Test 1: ", selectedIngredients.length) if (!exists) { setSelectedIngredients([...selectedIngredients, newIngredient]); } + console.log("Test 2: ", selectedIngredients.length) + ChangeAvailableSize(false) }; const RemoveIngredient = (idIngredient: number) => { + console.log("Test 1 Remove: ", selectedIngredients.length) const updatedIngredients = selectedIngredients.filter((ingredient) => ingredient.id !== idIngredient); setSelectedIngredients(updatedIngredients); + console.log("Test 2 Remove: ", selectedIngredients.length) + ChangeAvailableSize(true) }; + const ChangeAvailableSize = (remove: boolean) => { + if(remove){ + if (selectedIngredients.length == 1){ + setAvailableSize(0) + } + else if (selectedIngredients.length == 2){ + setAvailableSize(90) + } + else if (selectedIngredients.length == 3){ + setAvailableSize(180) + } + else if (selectedIngredients.length == 4){ + setAvailableSize(260) + } + else{ + setAvailableSize(280) + } + } + else{ + if (selectedIngredients.length == 0){ + setAvailableSize(90) + } + else if (selectedIngredients.length == 1){ + setAvailableSize(180) + } + else if (selectedIngredients.length == 2){ + setAvailableSize(260) + } + else{ + setAvailableSize(280) + } + } + } + const handleLetterPress = async (letter: string) => { try { const ingredientsByLetter = await ingredientService.getIngredientByLetter(letter); @@ -106,6 +147,24 @@ const loadIngredients = async () => { } }; + const changeListVisibility = () => { + if(listVisibility == "none"){ + setListVisibility("flex") + } + else{ + setListVisibility("none") + } + }; + + const changeAvailableVisibility = () => { + if(availableVisibility == "none"){ + setAvailableVisibility("flex") + } + else{ + setAvailableVisibility("none") + } + }; + const styles = StyleSheet.create({ linearGradient: { width: "100%", @@ -136,46 +195,56 @@ const loadIngredients = async () => { - - {alphabetArray.map((source, index) => ( - handleLetterPress(source)}> - {source} - - ))} - - - + + + + - - ( - - )} - keyExtractor={(item, index) => index.toString()} - ListEmptyComponent={() => ( - isLoading ? : Erreur lors du traitement des données - )} - style={{ flex: 1 }} - /> - + + + {alphabetArray.map((source, index) => ( + handleLetterPress(source)}> + {source} + + ))} + + + + + + ( + + )} + keyExtractor={(item, index) => index.toString()} + ListEmptyComponent={() => ( + isLoading ? : Erreur lors du traitement des données + )} + style={{ flex: 1 }} + /> + + - - Available - - + + + Available + + + + ( @@ -188,7 +257,7 @@ const loadIngredients = async () => { - + diff --git a/LeftOvers/screens/Profiles.tsx b/LeftOvers/screens/Profiles.tsx index b6f906f..3705fc7 100644 --- a/LeftOvers/screens/Profiles.tsx +++ b/LeftOvers/screens/Profiles.tsx @@ -8,6 +8,8 @@ import ProfileDetails from '../components/ProfileDetails'; import ProfileDelete from '../components/ProfileDelete'; import ColorContext from '../theme/ColorContext'; import AsyncStorage from '@react-native-async-storage/async-storage'; +import EventEmitter from './EventEmitter'; +import Profil from '../Models/Profil'; export default function Profiles({navigation, props}) { const { colors, toggleColors } = useContext(ColorContext) @@ -46,13 +48,18 @@ export default function Profiles({navigation, props}) { return []; } } + + const fetchProfiles = async () => { + const existingProfiles = await handleGetProfiles(); + setProfiles(existingProfiles); + }; + + const subscription = EventEmitter.addListener('profileAdded', async () => { + fetchProfiles(); + }); useEffect(() => { - const fetchProfiles = async () => { - const existingProfiles = await handleGetProfiles(); - setProfiles(existingProfiles); - }; - + handleDeleteProfiles(); fetchProfiles(); }, []); @@ -81,7 +88,7 @@ export default function Profiles({navigation, props}) { modal: { position: 'absolute', - top: '8%', + top: '0%', justifyContent: "center", alignItems: "center", width: "100%", @@ -170,12 +177,12 @@ export default function Profiles({navigation, props}) { const profileComponents = profiles.map((profile, index) => ( + name={profile.name} + avatar={profile.avatar} + diets={profile.diets} + allergies={profile.allergies} + onDeleteProfile={() => raisePopUp()} // <- Modification ici + /> {index < profiles.length - 1 && } )); diff --git a/LeftOvers/theme/colors.ts b/LeftOvers/theme/colors.ts index 878e4d9..e329e22 100644 --- a/LeftOvers/theme/colors.ts +++ b/LeftOvers/theme/colors.ts @@ -36,6 +36,7 @@ export interface Theme { buttonMain: string, yesButton: string, letterFilter: string, + foodElementBorder: string, } export const LightTheme : Theme = { @@ -64,6 +65,7 @@ export const LightTheme : Theme = { buttonMain: Ecru, yesButton: Moonstone, letterFilter: Moonstone, + foodElementBorder: Jet, } export const DarkTheme : Theme = { @@ -92,5 +94,6 @@ export const DarkTheme : Theme = { buttonMain: CarolinaBlue, yesButton: CarolinaBlue, letterFilter: CarolinaBlue, + foodElementBorder: CarolinaBlue, }