Skip to content

Commit

Permalink
Merge branch 'main' into CommentLogin/SignUp
Browse files Browse the repository at this point in the history
  • Loading branch information
MostafaMohAli committed Apr 24, 2023
2 parents 1c84ebb + 99127f2 commit 6fedf1d
Show file tree
Hide file tree
Showing 18 changed files with 180 additions and 75 deletions.
10 changes: 5 additions & 5 deletions Screens/DiaryScreen.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ const DiaryScreen = ({navigation}) => {
<ScrollView>
{entry.getBreakfast().map((food) => (
<DiaryTile
key={food.getName}
key={food.getName()}
foodItem = {food}
meal = {entry.getBreakfast()}
></DiaryTile>
Expand All @@ -75,7 +75,7 @@ const DiaryScreen = ({navigation}) => {
<ScrollView>
{entry.getLunch().map((food) => (
<DiaryTile
key={food.getName}
key={food.getName()}
foodItem = {food}
meal = {entry.getLunch()}
></DiaryTile>
Expand All @@ -89,7 +89,7 @@ const DiaryScreen = ({navigation}) => {
<ScrollView>
{entry.getDinner().map((food) => (
<DiaryTile
key={food.getName}
key={food.getName()}
foodItem = {food}
meal = {entry.getDinner()}
></DiaryTile>
Expand All @@ -104,7 +104,7 @@ const DiaryScreen = ({navigation}) => {
<ScrollView>
{entry.getSnacks().map((food) => (
<DiaryTile
key={food.getName}
key={food.getName()}
foodItem = {food}
meal = {entry.getSnacks()}
></DiaryTile>
Expand Down Expand Up @@ -179,4 +179,4 @@ const styles = StyleSheet.create({

})

export default DiaryScreen
export default DiaryScreen
22 changes: 14 additions & 8 deletions Screens/Home.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,20 @@ export default function Home({ navigation }) {
const [caloriesConsumed, setCaloriesConsumed] = React.useState("0");
const [goal, setGoal] = React.useState("");
const {diary, setDiary} = useContext(DiaryContext)

useFocusEffect(React.useCallback(() => {
getDb();
loadDiary();
}, []));

const loadDiary = async () =>{
await getDb()
let newDiary = await getDiary()
setCaloriesConsumed(newDiary.getEntry(new Date().toDateString()).getCalorieTotal())
console.log(calorieGoal)
newDiary.calorieGoal = cal

setDiary(newDiary);
}
const getDb = async () => {
uid = await AsyncStorage.getItem("uid");

Expand All @@ -44,18 +54,14 @@ export default function Home({ navigation }) {
setName(`${user.data()?.firstName} ${user.data()?.lastName}`);
setCalorieGoal(`${user.data()?.calorieGoal}`);
setGoal(`${user.data()?.goal}`);
newDiary.calorieGoal = `${user.data()?.calorieGoal}`
//TODO: remove log
console.log("set user name in if")
cal = `${user.data()?.calorieGoal}`
} else {
setName(user.data().name);
//TODO: remove log
console.log("set user name in else")
}
}catch(e){
setCalorieGoal(2000)
console.log(e)
}
setDiary(newDiary);

};

Expand Down Expand Up @@ -124,4 +130,4 @@ const styles = StyleSheet.create({
width: 20,
height: 20,
}
});
});
2 changes: 1 addition & 1 deletion Screens/Login.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ export default function Login({ navigation }) {
</Button>
<Button
style={styles.login}
onPress={() => navigate("RecoverPassword")}>
onPress={() => navigation.navigate("RecoverPassword")}>
Forgot Password?
</Button>
</View></>
Expand Down
103 changes: 77 additions & 26 deletions Screens/Settings/AccountSettings.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
//A screen to represent the user's current settings for their account.

import React, { useState, useEffect } from "react";
import { getAuth, updateEmail, updatePassword, reauthenticateWithCredential, EmailAuthProvider, signInWithEmailAndPassword, onAuthStateChanged} from "firebase/auth"
import { getAuth, updateEmail, updatePassword, reauthenticateWithCredential, EmailAuthProvider, signInWithEmailAndPassword } from "firebase/auth"
import { View, FlatList, TouchableWithoutFeedback, Keyboard, Button } from "react-native";
import { globalStyles } from "../../styles/global";
import Setting from "../../components/Setting";
Expand All @@ -10,63 +12,82 @@ import AsyncStorage from "@react-native-async-storage/async-storage";

export default function AccountSettings({ navigation }) {

//An array of settings objects to represent the settings to display on the screen.
const [settings, setSettings] = React.useState([
{settingName: "First Name", dbField: dbConstants.FIRST_NAME, data: null, key: "1"},
{settingName: "Last Name", dbField: dbConstants.LAST_NAME, data: null, key: "2"},
{settingName: "Email", dbField: dbConstants.EMAIL, data: null, key: "3"},
{settingName: "Password", dbField: dbConstants.PASSWORD, data: null, key: "4"},
]);

const [prevPassword, setPrevPassword] = React.useState(null)
const [prevEmail, setPrevEmail] = React.useState(null)

//A state variable to represent this user's document snapshot as it exists in Firebase.
const [userDocSnap, setUserDocSnap] = useState(null);

//A state variable to hold this app's auth instance.
const [auth, setAuth] = useState(getAuth())

//Ensure an up-to-date version of the user's document snapshot is held after each render of this component.
useEffect(() => {
getUserDocSnap();
}, []);

//necessary because user may update same setting during same session multiple times
//Whenever the user changes their settings, refresh their document snapshot to ensure the data is accurate.
//This is necessary because user may update same setting during same session multiple times
useEffect(() => {
getUserDocSnap();
}, [settings]);

//Observe the settings object and save changes each time an edit is detected.
//Allows for automatic saving.
useEffect(() => {
saveChangesHandler();
}, [settings]);

//Retrieve the current user's document snapshot from Firebase.
const getUserDocSnap = async () => {
const uid = await AsyncStorage.getItem("uid");
const docRef = doc(db, "users", uid);
const snap = await getDoc(docRef);
setUserDocSnap(snap);
};

//Handles saving the settings displayed on this screen to Firebase.
const saveChangesHandler = async () => {

//Check if the email setting is null. If not, i.e., it has been changed, check that it's valid.
if (settings[2].data != null && !/^\S+@\S+\.\S+$/.test(settings[2].data)) {
alert("Changes to your email were not saved. Please enter a valid email address.");
return;
}

if (settings[3].data != null && settings[3].data.length < 6) {
alert("Changes to your password were not saved. Please enter at least six characters.")
return;
}


//handle email and password sequentially
if (settings[2].data != null) {
await handleEmailChange(settings[2].data).then(saveSettingsToFirestore());
} else if (settings[3].data != null) {
await handlePasswordChange(settings[3].data).then(saveSettingsToFirestore());
} else {
saveSettingsToFirestore();
}
//Check if the password setting is null. If not, i.e., it has been changed, check that it's valid.
if (settings[3].data != null && settings[3].data.length < 6) {
alert("Changes to your password were not saved. Please enter at least six characters.")
return;
}


//Check if email or password have been changed. These require special care to reconcile them with Firebase Authenticator.
//If so, then call their respective methods to change them.
//If not, then call saveSettingsToFirestore() to save all non-sensitive settings to firestore.
if (settings[2].data != null) {
await handleEmailChange(settings[2].data).then(saveSettingsToFirestore());
} else if (settings[3].data != null) {
await handlePasswordChange(settings[3].data).then(saveSettingsToFirestore());
} else {
saveSettingsToFirestore();
}
}

//Take all settings objects stored in state and change their respective Firestore entries.
const saveSettingsToFirestore = async () => {
const uid = await AsyncStorage.getItem("uid");
const userDocRef = doc(db, "users", uid);

//For each element whose data is not null, update its respective Firestore field.
settings.forEach(async element => {
if (element.data != null) {
await updateDoc(userDocRef, {
Expand All @@ -79,44 +100,74 @@ export default function AccountSettings({ navigation }) {
});
}

//Handles change of user email.
const handleEmailChange = async (newEmail) => {
//refresh userDocSnap to ensure up-to-date info
const email = userDocSnap.get(dbConstants.EMAIL)
console.log(email)
const password = userDocSnap.get(dbConstants.PASSWORD)

//Get the user's current email and password.
let email = prevEmail
let password = prevPassword
if (prevEmail == null) {
email = userDocSnap.get(dbConstants.EMAIL)
}
if (prevPassword == null) {
password = userDocSnap.get(dbConstants.PASSWORD)
}

//Create a credential object with the user's email and password.
const credential = EmailAuthProvider.credential(email, password)
console.log("Obtained credential: " + credential)

console.log("user's email is " + email + " and password is " + password)

//Sign the user in with their email and password to ensure they are recently signed in.
signInWithEmailAndPassword(auth, email, password).then( (user) => {
console.log("signed in successfully")
//Reauthenticate the user with the credential object to allow changing of sensitive information (email, password).
reauthenticateWithCredential(auth.currentUser, credential).then(() => {
//update email

//Update the user's email, and catch the error if it fails.
console.log("attempting email update to " + newEmail)
updateEmail(auth.currentUser, newEmail).catch((error) => {
console.log("Email update failed after reauthentication attempt.")
}).then(() => {
setPrevEmail(newEmail);
})}
)})
}

//Handles change of user password.
const handlePasswordChange = async (newPassword) => {
const email = userDocSnap.get(dbConstants.EMAIL)
const password = userDocSnap.get(dbConstants.PASSWORD)

//Get the user's current email and password.
let email = prevEmail
let password = prevPassword
if (prevEmail == null) {
email = userDocSnap.get(dbConstants.EMAIL)
}
if (prevPassword == null) {
password = userDocSnap.get(dbConstants.PASSWORD)
}

console.log("user's email is " + email + " and password is " + password)

//Create a credential object with the user's email and password.
const credential = EmailAuthProvider.credential(email, password)
console.log("Obtained credential: " + credential)

//Sign the user in with their email and password to ensure they are recently signed in.
signInWithEmailAndPassword(auth, email, password).then( (user) => {
console.log("signed in successfully")
//Reauthenticate the user with the credential object to allow changing of sensitive information (email, password).
reauthenticateWithCredential(auth.currentUser, credential).then(() => {
//update email
//Update the user's password, and catch the error if it fails.
console.log("attempting password update to " + newPassword)
updatePassword(auth.currentUser, newPassword).catch((error) => {
console.log("Password update failed after reauthentication attempt.")
}).then(() => {
setPrevPassword(newPassword);
})}
)})
}

//This callback is passed to child setting components to update the settings objects stored in this screen's State.
const handleCallback = (item, newData) => {
const newSettings = [...settings];
const index = newSettings.indexOf(item);
Expand Down
5 changes: 3 additions & 2 deletions Screens/Settings/BiometricsSettings.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
//A screen to represent the user's current settings for their biometrics.

import React, { useState, useEffect } from "react";
import { View, FlatList, TouchableWithoutFeedback, Keyboard, Button } from "react-native";
import { View, FlatList, TouchableWithoutFeedback, Keyboard } from "react-native";
import { globalStyles } from "../../styles/global";
import NumericSetting from "../../components/NumericSetting";
import { doc, getDoc, updateDoc } from "firebase/firestore";
Expand All @@ -8,7 +10,6 @@ import * as dbConstants from "../../DatabaseConstants";
import RadioSetting from "../../components/RadioSetting"
import AsyncStorage from "@react-native-async-storage/async-storage";
import { ActivityLevels } from "../../constants/ActivityLevels";
import {COLORS} from '../../constants/colors'

export default function BiometricsSettings({ navigation }) {

Expand Down
16 changes: 15 additions & 1 deletion Screens/Settings/DietaryRestrictionsSettings.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
//A screen to represent the user's current settings for their dietary restrictions.

import React, { useState, useEffect } from "react";
import { ScrollView, View, FlatList, TouchableWithoutFeedback, Keyboard, Button } from "react-native";
import { View, FlatList } from "react-native";
import { globalStyles } from "../../styles/global";
import { doc, getDoc, updateDoc } from "firebase/firestore";
import { db } from "../../firebase";
Expand All @@ -10,10 +12,13 @@ import CheckboxSetting from "../../components/CheckboxSetting";

export default function DietaryRestrictionsSettings({ navigation }) {

//A state variable to hold this screen's list of checkbox settings
const [checkboxSettings, setCheckboxSettings] = React.useState(null);

//A state variable to represent this user's document snapshot as it exists in Firebase.
const [userDocSnap, setUserDocSnap] = useState(null);

//Utility function to get a list of all of the settings objects from the DietaryRestrictions enum and turn them into an array of objects.
async function getSettingsObjects() {
const dietaryRestrictionsArray = Object.values(DietaryRestrictions);
const currentRestrictions = userDocSnap.get(dbConstants.DIETARY_RESTRICTIONS)
Expand All @@ -30,10 +35,13 @@ export default function DietaryRestrictionsSettings({ navigation }) {
return objectArray;
}

//Hook to ensure an up-to-date version of the user's document snapshot is held after each render of this component.
useEffect(() => {
getUserDocSnap();
}, []);


//If the user doc snap is not null, set the checkbox settings after converting them to settings objects.
useEffect(() => {
if (userDocSnap != null) {
getSettingsObjects().then((settings) => {
Expand All @@ -47,31 +55,37 @@ export default function DietaryRestrictionsSettings({ navigation }) {
saveChangesHandler();
}, [checkboxSettings]);

//Retrieve the current user's document snapshot from Firebase.
const getUserDocSnap = async () => {
const uid = await AsyncStorage.getItem("uid");
const docRef = doc(db, "users", uid);
const snap = await getDoc(docRef);
setUserDocSnap(snap);
};

//Handles saving the settings displayed on this screen to Firebase.
const saveChangesHandler = async () => {
if (checkboxSettings == null) return;
const uid = await AsyncStorage.getItem("uid");
const userDocRef = doc(db, "users", uid);

//Filter the checkedBoxes state variable for only boxes that are checked, and put them into an array
const checkedBoxes = checkboxSettings.filter(box => box.isChecked);
const checkedBoxesStrings = [];
checkedBoxes.forEach(box => {
checkedBoxesStrings.push(box.settingName)
});

//Join the string array into one string
const restrictionsString = checkedBoxesStrings.join(", ");

//Store the string created above
await updateDoc(userDocRef, {
[dbConstants.DIETARY_RESTRICTIONS]: restrictionsString
});
}

//This callback is passed to child setting components to update the settings objects stored in this screen's State.
const handleCheckboxCallback = (item, newData) => {
const newCheckboxSettings = [...checkboxSettings];
const index = newCheckboxSettings.indexOf(item);
Expand Down
Loading

0 comments on commit 6fedf1d

Please sign in to comment.