fixed settings paging, calendar style,

This commit is contained in:
ivic00
2024-10-29 20:15:20 +01:00
parent 8ba6f7aecf
commit 74d82e2029
9 changed files with 1934 additions and 1729 deletions

View File

@ -23,9 +23,16 @@ import NavToDosIcon from "@/assets/svgs/NavToDosIcon";
import NavBrainDumpIcon from "@/assets/svgs/NavBrainDumpIcon"; import NavBrainDumpIcon from "@/assets/svgs/NavBrainDumpIcon";
import NavCalendarIcon from "@/assets/svgs/NavCalendarIcon"; import NavCalendarIcon from "@/assets/svgs/NavCalendarIcon";
import NavSettingsIcon from "@/assets/svgs/NavSettingsIcon"; import NavSettingsIcon from "@/assets/svgs/NavSettingsIcon";
import { useAtom } from "jotai";
import {
settingsPageIndex,
userSettingsView,
} from "@/components/pages/calendar/atoms";
export default function TabLayout() { export default function TabLayout() {
const { mutateAsync: signOut } = useSignOut(); const { mutateAsync: signOut } = useSignOut();
const [pageIndex, setPageIndex] = useAtom(settingsPageIndex);
const [userView, setUserView] = useAtom(userSettingsView);
return ( return (
<Drawer <Drawer
@ -66,14 +73,22 @@ export default function TabLayout() {
title={"Calendar"} title={"Calendar"}
color="rgb(7, 184, 199)" color="rgb(7, 184, 199)"
bgColor={"rgb(231, 248, 250)"} bgColor={"rgb(231, 248, 250)"}
pressFunc={() => props.navigation.navigate("calendar")} pressFunc={() => {
props.navigation.navigate("calendar");
setPageIndex(0);
setUserView(true);
}}
icon={<NavCalendarIcon />} icon={<NavCalendarIcon />}
/> />
<DrawerButton <DrawerButton
color="#50be0c" color="#50be0c"
title={"Groceries"} title={"Groceries"}
bgColor={"#eef9e7"} bgColor={"#eef9e7"}
pressFunc={() => props.navigation.navigate("grocery")} pressFunc={() => {
props.navigation.navigate("grocery");
setPageIndex(0);
setUserView(true);
}}
icon={<NavGroceryIcon />} icon={<NavGroceryIcon />}
/> />
</View> </View>
@ -95,21 +110,33 @@ export default function TabLayout() {
color="#8005eb" color="#8005eb"
title={"To Do's"} title={"To Do's"}
bgColor={"#f3e6fd"} bgColor={"#f3e6fd"}
pressFunc={() => props.navigation.navigate("todos")} pressFunc={() => {
props.navigation.navigate("todos");
setPageIndex(0);
setUserView(true);
}}
icon={<NavToDosIcon />} icon={<NavToDosIcon />}
/> />
<DrawerButton <DrawerButton
color="#e0ca03" color="#e0ca03"
title={"Brain Dump"} title={"Brain Dump"}
bgColor={"#fffacb"} bgColor={"#fffacb"}
pressFunc={() => props.navigation.navigate("brain_dump")} pressFunc={() => {
props.navigation.navigate("brain_dump");
setPageIndex(0);
setUserView(true);
}}
icon={<NavBrainDumpIcon />} icon={<NavBrainDumpIcon />}
/> />
{/*<DrawerItem label="Logout" onPress={() => signOut()} />*/} {/*<DrawerItem label="Logout" onPress={() => signOut()} />*/}
</View> </View>
</View> </View>
<Button <Button
onPress={() => props.navigation.navigate("settings")} onPress={() => {
props.navigation.navigate("settings");
setPageIndex(0);
setUserView(true);
}}
label={"Manage Settings"} label={"Manage Settings"}
labelStyle={styles.label} labelStyle={styles.label}
iconSource={() => ( iconSource={() => (

View File

@ -1,17 +1,24 @@
import React, {memo} from "react"; import React, { memo } from "react";
import {Button, Picker, PickerModes, SegmentedControl, Text, View,} from "react-native-ui-lib"; import {
import {MaterialIcons} from "@expo/vector-icons"; Button,
import {modeMap, months} from "./constants"; Picker,
import {StyleSheet} from "react-native"; PickerModes,
import {useAtom} from "jotai"; SegmentedControl,
import {modeAtom, selectedDateAtom} from "@/components/pages/calendar/atoms"; Text,
import {isSameDay} from "date-fns"; View,
import {useAuthContext} from "@/contexts/AuthContext"; } from "react-native-ui-lib";
import { MaterialIcons } from "@expo/vector-icons";
import { modeMap, months } from "./constants";
import { StyleSheet } from "react-native";
import { useAtom } from "jotai";
import { modeAtom, selectedDateAtom } from "@/components/pages/calendar/atoms";
import { format, isSameDay } from "date-fns";
import { useAuthContext } from "@/contexts/AuthContext";
export const CalendarHeader = memo(() => { export const CalendarHeader = memo(() => {
const [selectedDate, setSelectedDate] = useAtom(selectedDateAtom); const [selectedDate, setSelectedDate] = useAtom(selectedDateAtom);
const [mode, setMode] = useAtom(modeAtom); const [mode, setMode] = useAtom(modeAtom);
const {profileData} = useAuthContext(); const { profileData } = useAuthContext();
const handleSegmentChange = (index: number) => { const handleSegmentChange = (index: number) => {
const selectedMode = modeMap.get(index); const selectedMode = modeMap.get(index);
@ -49,23 +56,23 @@ export const CalendarHeader = memo(() => {
}} }}
> >
<View row centerV gap-3> <View row centerV gap-3>
<Text style={{fontFamily: "Manrope_500Medium", fontSize: 17}}> <Text style={{ fontFamily: "Manrope_500Medium", fontSize: 17 }}>
{selectedDate.getFullYear()} {selectedDate.getFullYear()}
</Text> </Text>
<Picker <Picker
value={months[selectedDate.getMonth()]} value={months[selectedDate.getMonth()]}
placeholder={"Select Month"} placeholder={"Select Month"}
style={{fontFamily: "Manrope_500Medium", fontSize: 17, width: 85}} style={{ fontFamily: "Manrope_500Medium", fontSize: 17, width: 85 }}
mode={PickerModes.SINGLE} mode={PickerModes.SINGLE}
onChange={(itemValue) => handleMonthChange(itemValue as string)} onChange={(itemValue) => handleMonthChange(itemValue as string)}
trailingAccessory={<MaterialIcons name={"keyboard-arrow-down"}/>} trailingAccessory={<MaterialIcons name={"keyboard-arrow-down"} />}
topBarProps={{ topBarProps={{
title: selectedDate.getFullYear().toString(), title: selectedDate.getFullYear().toString(),
titleStyle: {fontFamily: "Manrope_500Medium", fontSize: 17}, titleStyle: { fontFamily: "Manrope_500Medium", fontSize: 17 },
}} }}
> >
{months.map((month) => ( {months.map((month) => (
<Picker.Item key={month} label={month} value={month}/> <Picker.Item key={month} label={month} value={month} />
))} ))}
</Picker> </Picker>
</View> </View>
@ -82,16 +89,14 @@ export const CalendarHeader = memo(() => {
borderWidth: 0.7, borderWidth: 0.7,
borderColor: "#dadce0", borderColor: "#dadce0",
height: 30, height: 30,
paddingHorizontal: 10 paddingHorizontal: 10,
}} }}
labelStyle={{ labelStyle={{
fontSize: 12, fontSize: 12,
color: "black", color: "black",
fontFamily: "Manrope_500Medium", fontFamily: "Manrope_500Medium",
}} }}
label={new Date().toLocaleDateString("en-US", { label={format(new Date(), "dd/MM/yyyy")}
timeZone: profileData?.timeZone || "",
})}
onPress={() => { onPress={() => {
setSelectedDate(new Date()); setSelectedDate(new Date());
}} }}
@ -100,7 +105,7 @@ export const CalendarHeader = memo(() => {
<View> <View>
<SegmentedControl <SegmentedControl
segments={[{label: "D"}, {label: "W"}, {label: "M"}]} segments={[{ label: "D" }, { label: "W" }, { label: "M" }]}
backgroundColor="#ececec" backgroundColor="#ececec"
inactiveColor="#919191" inactiveColor="#919191"
activeBackgroundColor="#ea156c" activeBackgroundColor="#ea156c"

View File

@ -1,8 +1,8 @@
import React, {useCallback, useEffect, useMemo, useState} from "react"; import React, { useCallback, useEffect, useMemo, useState } from "react";
import {Calendar} from "react-native-big-calendar"; import { Calendar } from "react-native-big-calendar";
import {ActivityIndicator, StyleSheet, View, ViewStyle} from "react-native"; import { ActivityIndicator, StyleSheet, View, ViewStyle } from "react-native";
import {useGetEvents} from "@/hooks/firebase/useGetEvents"; import { useGetEvents } from "@/hooks/firebase/useGetEvents";
import {useAtom, useSetAtom} from "jotai"; import { useAtom, useSetAtom } from "jotai";
import { import {
editVisibleAtom, editVisibleAtom,
eventForEditAtom, eventForEditAtom,
@ -10,9 +10,9 @@ import {
selectedDateAtom, selectedDateAtom,
selectedNewEventDateAtom, selectedNewEventDateAtom,
} from "@/components/pages/calendar/atoms"; } from "@/components/pages/calendar/atoms";
import {useAuthContext} from "@/contexts/AuthContext"; import { useAuthContext } from "@/contexts/AuthContext";
import {CalendarEvent} from "@/components/pages/calendar/interfaces"; import { CalendarEvent } from "@/components/pages/calendar/interfaces";
import {Text} from "react-native-ui-lib"; import { Text } from "react-native-ui-lib";
interface EventCalendarProps { interface EventCalendarProps {
calendarHeight: number; calendarHeight: number;
@ -26,9 +26,9 @@ const getTotalMinutes = () => {
}; };
export const EventCalendar: React.FC<EventCalendarProps> = React.memo( export const EventCalendar: React.FC<EventCalendarProps> = React.memo(
({calendarHeight}) => { ({ calendarHeight }) => {
const {data: events, isLoading} = useGetEvents(); const { data: events, isLoading } = useGetEvents();
const {profileData} = useAuthContext(); const { profileData } = useAuthContext();
const [selectedDate, setSelectedDate] = useAtom(selectedDateAtom); const [selectedDate, setSelectedDate] = useAtom(selectedDateAtom);
const [mode, setMode] = useAtom(modeAtom); const [mode, setMode] = useAtom(modeAtom);
@ -39,7 +39,7 @@ export const EventCalendar: React.FC<EventCalendarProps> = React.memo(
const [isRendering, setIsRendering] = useState(true); const [isRendering, setIsRendering] = useState(true);
const [offsetMinutes, setOffsetMinutes] = useState(getTotalMinutes()); const [offsetMinutes, setOffsetMinutes] = useState(getTotalMinutes());
const todaysDate = new Date() const todaysDate = new Date();
useEffect(() => { useEffect(() => {
if (events && mode) { if (events && mode) {
@ -55,7 +55,7 @@ export const EventCalendar: React.FC<EventCalendarProps> = React.memo(
(event: CalendarEvent) => { (event: CalendarEvent) => {
if (mode === "day" || mode === "week") { if (mode === "day" || mode === "week") {
setEditVisible(true); setEditVisible(true);
console.log({event}); console.log({ event });
setEventForEdit(event); setEventForEdit(event);
} else { } else {
setMode("day"); setMode("day");
@ -85,7 +85,7 @@ export const EventCalendar: React.FC<EventCalendarProps> = React.memo(
); );
const memoizedEventCellStyle = useCallback( const memoizedEventCellStyle = useCallback(
(event: CalendarEvent) => ({backgroundColor: event.eventColor}), (event: CalendarEvent) => ({ backgroundColor: event.eventColor }),
[] []
); );
@ -107,7 +107,7 @@ export const EventCalendar: React.FC<EventCalendarProps> = React.memo(
}, [selectedDate, mode]); }, [selectedDate, mode]);
const dateStyle = useMemo(() => { const dateStyle = useMemo(() => {
if (mode === "week") return undefined if (mode === "week") return undefined;
return isSameDate(todaysDate, selectedDate) && mode === "day" return isSameDate(todaysDate, selectedDate) && mode === "day"
? styles.dayHeader ? styles.dayHeader
: styles.otherDayHeader; : styles.otherDayHeader;
@ -161,9 +161,9 @@ export const EventCalendar: React.FC<EventCalendarProps> = React.memo(
const appliedStyle = isCurrentDate ? currentDateStyle : defaultStyle; const appliedStyle = isCurrentDate ? currentDateStyle : defaultStyle;
return ( return (
<View style={{alignItems: "center"}}> <View style={{ alignItems: "center" }}>
<View style={appliedStyle}> <View style={appliedStyle}>
<Text style={{color: isCurrentDate ? "white" : "black"}}> <Text style={{ color: isCurrentDate ? "white" : "black" }}>
{date.getDate()} {date.getDate()}
</Text> </Text>
</View> </View>
@ -183,7 +183,7 @@ export const EventCalendar: React.FC<EventCalendarProps> = React.memo(
if (isLoading || isRendering) { if (isLoading || isRendering) {
return ( return (
<View style={styles.loadingContainer}> <View style={styles.loadingContainer}>
<ActivityIndicator size="large" color="#0000ff"/> <ActivityIndicator size="large" color="#0000ff" />
</View> </View>
); );
} }
@ -205,8 +205,29 @@ export const EventCalendar: React.FC<EventCalendarProps> = React.memo(
headerContentStyle={memoizedHeaderContentStyle} headerContentStyle={memoizedHeaderContentStyle}
onSwipeEnd={handleSwipeEnd} onSwipeEnd={handleSwipeEnd}
scrollOffsetMinutes={offsetMinutes} scrollOffsetMinutes={offsetMinutes}
theme={{
palette: {
nowIndicator: "#fd1575",
gray: {
"100": "#e8eaed",
"200": "#e8eaed",
"500": "#b7b7b7",
"800": "#919191",
},
},
typography: {
fontFamily: "PlusJakartaSans_500Medium",
sm: { fontFamily: "Manrope_600SemiBold", fontSize: 15 },
xl: {
fontFamily: "PlusJakartaSans_500Medium",
fontSize: 16,
},
moreLabel: {},
xs:{fontSize: 10}
},
}}
dayHeaderStyle={dateStyle} dayHeaderStyle={dateStyle}
dayHeaderHighlightColor={dayHeaderColor} dayHeaderHighlightColor={"white"}
renderCustomDateForMonth={renderCustomDateForMonth} renderCustomDateForMonth={renderCustomDateForMonth}
showAdjacentMonths showAdjacentMonths
/> />

View File

@ -1,5 +1,5 @@
import { atom } from 'jotai'; import { atom } from "jotai";
import {CalendarEvent} from "@/components/pages/calendar/interfaces"; import { CalendarEvent } from "@/components/pages/calendar/interfaces";
export const editVisibleAtom = atom<boolean>(false); export const editVisibleAtom = atom<boolean>(false);
export const eventForEditAtom = atom<CalendarEvent | undefined>(undefined); export const eventForEditAtom = atom<CalendarEvent | undefined>(undefined);
@ -7,3 +7,5 @@ export const isFamilyViewAtom = atom<boolean>(false);
export const modeAtom = atom<"week" | "month" | "day">("week"); export const modeAtom = atom<"week" | "month" | "day">("week");
export const selectedDateAtom = atom<Date>(new Date()); export const selectedDateAtom = atom<Date>(new Date());
export const selectedNewEventDateAtom = atom<Date | undefined>(undefined); export const selectedNewEventDateAtom = atom<Date | undefined>(undefined);
export const settingsPageIndex = atom<number>(0);
export const userSettingsView = atom<boolean>(true);

View File

@ -1,10 +1,10 @@
import {AntDesign, Ionicons} from "@expo/vector-icons"; import { AntDesign, Ionicons } from "@expo/vector-icons";
import React, {useCallback, useEffect, useState} from "react"; import React, { useCallback, useEffect, useState } from "react";
import {Button, Checkbox, Text, View} from "react-native-ui-lib"; import { Button, Checkbox, Text, View } from "react-native-ui-lib";
import {ActivityIndicator, ScrollView, StyleSheet} from "react-native"; import { ActivityIndicator, ScrollView, StyleSheet } from "react-native";
import {TouchableOpacity} from "react-native-gesture-handler"; import { TouchableOpacity } from "react-native-gesture-handler";
import {useAuthContext} from "@/contexts/AuthContext"; import { useAuthContext } from "@/contexts/AuthContext";
import {useUpdateUserData} from "@/hooks/firebase/useUpdateUserData"; import { useUpdateUserData } from "@/hooks/firebase/useUpdateUserData";
import debounce from "debounce"; import debounce from "debounce";
import AppleIcon from "@/assets/svgs/AppleIcon"; import AppleIcon from "@/assets/svgs/AppleIcon";
import GoogleIcon from "@/assets/svgs/GoogleIcon"; import GoogleIcon from "@/assets/svgs/GoogleIcon";
@ -12,13 +12,15 @@ import OutlookIcon from "@/assets/svgs/OutlookIcon";
import * as AuthSession from "expo-auth-session"; import * as AuthSession from "expo-auth-session";
import * as Google from "expo-auth-session/providers/google"; import * as Google from "expo-auth-session/providers/google";
import * as WebBrowser from "expo-web-browser"; import * as WebBrowser from "expo-web-browser";
import {UserProfile} from "@firebase/auth"; import { UserProfile } from "@firebase/auth";
import {useFetchAndSaveGoogleEvents} from "@/hooks/useFetchAndSaveGoogleEvents"; import { useFetchAndSaveGoogleEvents } from "@/hooks/useFetchAndSaveGoogleEvents";
import {useFetchAndSaveOutlookEvents} from "@/hooks/useFetchAndSaveOutlookEvents"; import { useFetchAndSaveOutlookEvents } from "@/hooks/useFetchAndSaveOutlookEvents";
import {useFetchAndSaveAppleEvents} from "@/hooks/useFetchAndSaveAppleEvents"; import { useFetchAndSaveAppleEvents } from "@/hooks/useFetchAndSaveAppleEvents";
import * as AppleAuthentication from 'expo-apple-authentication'; import * as AppleAuthentication from "expo-apple-authentication";
import ExpoLocalization from "expo-localization/src/ExpoLocalization"; import ExpoLocalization from "expo-localization/src/ExpoLocalization";
import {colorMap} from "@/constants/colorMap"; import { colorMap } from "@/constants/colorMap";
import { useAtom } from "jotai";
import { settingsPageIndex } from "../calendar/atoms";
const googleConfig = { const googleConfig = {
androidClientId: androidClientId:
@ -36,24 +38,29 @@ const googleConfig = {
const microsoftConfig = { const microsoftConfig = {
clientId: "13c79071-1066-40a9-9f71-b8c4b138b4af", clientId: "13c79071-1066-40a9-9f71-b8c4b138b4af",
redirectUri: AuthSession.makeRedirectUri({path: "settings"}), redirectUri: AuthSession.makeRedirectUri({ path: "settings" }),
scopes: [ scopes: [
"openid", "openid",
"profile", "profile",
"email", "email",
"offline_access", "offline_access",
"Calendars.ReadWrite", "Calendars.ReadWrite",
"User.Read" "User.Read",
], ],
authorizationEndpoint: "https://login.microsoftonline.com/common/oauth2/v2.0/authorize", authorizationEndpoint:
"https://login.microsoftonline.com/common/oauth2/v2.0/authorize",
tokenEndpoint: "https://login.microsoftonline.com/common/oauth2/v2.0/token", tokenEndpoint: "https://login.microsoftonline.com/common/oauth2/v2.0/token",
}; };
const CalendarSettingsPage = (props: { const CalendarSettingsPage = () => {
setSelectedPage: (page: number) => void; const { profileData } = useAuthContext();
}) => { const [pageIndex, setPageIndex] = useAtom(settingsPageIndex);
const {profileData} = useAuthContext(); const [firstDayOfWeek, setFirstDayOfWeek] = useState<string>(
const [firstDayOfWeek, setFirstDayOfWeek] = useState<string>(profileData?.firstDayOfWeek ?? ExpoLocalization.getCalendars()[0].firstWeekday === 1 ? "Mondays" : "Sundays"); profileData?.firstDayOfWeek ??
ExpoLocalization.getCalendars()[0].firstWeekday === 1
? "Mondays"
: "Sundays"
);
const [selectedColor, setSelectedColor] = useState<string>( const [selectedColor, setSelectedColor] = useState<string>(
profileData?.eventColor ?? colorMap.pink profileData?.eventColor ?? colorMap.pink
@ -62,11 +69,15 @@ const CalendarSettingsPage = (props: {
profileData?.eventColor ?? colorMap.pink profileData?.eventColor ?? colorMap.pink
); );
const {mutateAsync: updateUserData} = useUpdateUserData(); const { mutateAsync: updateUserData } = useUpdateUserData();
const {mutateAsync: fetchAndSaveGoogleEvents, isLoading: isSyncingGoogle} = useFetchAndSaveGoogleEvents(); const { mutateAsync: fetchAndSaveGoogleEvents, isLoading: isSyncingGoogle } =
const {mutateAsync: fetchAndSaveOutlookEvents, isLoading: isSyncingOutlook} = useFetchAndSaveOutlookEvents(); useFetchAndSaveGoogleEvents();
const {mutateAsync: fetchAndSaveAppleEvents, isLoading: isSyncingApple} = useFetchAndSaveAppleEvents(); const {
mutateAsync: fetchAndSaveOutlookEvents,
isLoading: isSyncingOutlook,
} = useFetchAndSaveOutlookEvents();
const { mutateAsync: fetchAndSaveAppleEvents, isLoading: isSyncingApple } =
useFetchAndSaveAppleEvents();
WebBrowser.maybeCompleteAuthSession(); WebBrowser.maybeCompleteAuthSession();
const [_, response, promptAsync] = Google.useAuthRequest(googleConfig); const [_, response, promptAsync] = Google.useAuthRequest(googleConfig);
@ -83,7 +94,7 @@ const CalendarSettingsPage = (props: {
const userInfoResponse = await fetch( const userInfoResponse = await fetch(
"https://www.googleapis.com/oauth2/v3/userinfo", "https://www.googleapis.com/oauth2/v3/userinfo",
{ {
headers: {Authorization: `Bearer ${accessToken}`}, headers: { Authorization: `Bearer ${accessToken}` },
} }
); );
@ -91,13 +102,18 @@ const CalendarSettingsPage = (props: {
const googleMail = userInfo.email; const googleMail = userInfo.email;
let googleAccounts = profileData?.googleAccounts; let googleAccounts = profileData?.googleAccounts;
const updatedGoogleAccounts = googleAccounts ? {...googleAccounts, [googleMail]: accessToken} : {[googleMail]: accessToken}; const updatedGoogleAccounts = googleAccounts
? { ...googleAccounts, [googleMail]: accessToken }
: { [googleMail]: accessToken };
await updateUserData({ await updateUserData({
newUserData: {googleAccounts: updatedGoogleAccounts}, newUserData: { googleAccounts: updatedGoogleAccounts },
}); });
await fetchAndSaveGoogleEvents({token: accessToken, email: googleMail}) await fetchAndSaveGoogleEvents({
token: accessToken,
email: googleMail,
});
} }
} catch (error) { } catch (error) {
console.error("Error during Google sign-in:", error); console.error("Error during Google sign-in:", error);
@ -160,11 +176,14 @@ const CalendarSettingsPage = (props: {
console.log("Access token received, fetching user info..."); console.log("Access token received, fetching user info...");
// Fetch user info from Microsoft Graph API to get the email // Fetch user info from Microsoft Graph API to get the email
const userInfoResponse = await fetch("https://graph.microsoft.com/v1.0/me", { const userInfoResponse = await fetch(
"https://graph.microsoft.com/v1.0/me",
{
headers: { headers: {
Authorization: `Bearer ${tokenData.access_token}`, Authorization: `Bearer ${tokenData.access_token}`,
}, },
}); }
);
const userInfo = await userInfoResponse.json(); const userInfo = await userInfoResponse.json();
console.log("User info received:", userInfo); console.log("User info received:", userInfo);
@ -175,14 +194,19 @@ const CalendarSettingsPage = (props: {
const outlookMail = userInfo.mail || userInfo.userPrincipalName; const outlookMail = userInfo.mail || userInfo.userPrincipalName;
let microsoftAccounts = profileData?.microsoftAccounts; let microsoftAccounts = profileData?.microsoftAccounts;
const updatedMicrosoftAccounts = microsoftAccounts ? {...microsoftAccounts, [outlookMail]: tokenData.access_token} : {[outlookMail]: tokenData.access_token}; const updatedMicrosoftAccounts = microsoftAccounts
? { ...microsoftAccounts, [outlookMail]: tokenData.access_token }
: { [outlookMail]: tokenData.access_token };
// Update user data with Microsoft token and email // Update user data with Microsoft token and email
await updateUserData({ await updateUserData({
newUserData: {microsoftAccounts: updatedMicrosoftAccounts}, newUserData: { microsoftAccounts: updatedMicrosoftAccounts },
}); });
await fetchAndSaveOutlookEvents(tokenData.access_token, outlookMail) await fetchAndSaveOutlookEvents(
tokenData.access_token,
outlookMail
);
console.log("User data updated successfully."); console.log("User data updated successfully.");
} }
} }
@ -214,13 +238,15 @@ const CalendarSettingsPage = (props: {
console.log("Apple ID token received. Fetch user info if needed..."); console.log("Apple ID token received. Fetch user info if needed...");
await updateUserData({ await updateUserData({
newUserData: {appleToken, appleMail}, newUserData: { appleToken, appleMail },
}); });
console.log("User data updated with Apple ID token."); console.log("User data updated with Apple ID token.");
await fetchAndSaveAppleEvents({token: appleToken, email: appleMail!}); await fetchAndSaveAppleEvents({ token: appleToken, email: appleMail! });
} else { } else {
console.warn("Apple authentication was not successful or email was hidden."); console.warn(
"Apple authentication was not successful or email was hidden."
);
} }
} catch (error) { } catch (error) {
console.error("Error during Apple Sign-in:", error); console.error("Error during Apple Sign-in:", error);
@ -260,8 +286,10 @@ const CalendarSettingsPage = (props: {
const handleChangeFirstDayOfWeek = (firstDayOfWeek: string) => { const handleChangeFirstDayOfWeek = (firstDayOfWeek: string) => {
setFirstDayOfWeek(firstDayOfWeek === "Sundays" ? "Mondays" : "Sundays"); setFirstDayOfWeek(firstDayOfWeek === "Sundays" ? "Mondays" : "Sundays");
debouncedUpdateFirstDayOfWeek(firstDayOfWeek === "Sundays" ? "Mondays" : "Sundays"); debouncedUpdateFirstDayOfWeek(
} firstDayOfWeek === "Sundays" ? "Mondays" : "Sundays"
);
};
const handleChangeColor = (color: string) => { const handleChangeColor = (color: string) => {
setPreviousSelectedColor(selectedColor); setPreviousSelectedColor(selectedColor);
@ -269,7 +297,10 @@ const CalendarSettingsPage = (props: {
debouncedUpdateUserData(color); debouncedUpdateUserData(color);
}; };
const clearToken = async (provider: "google" | "outlook" | "apple", email: string) => { const clearToken = async (
provider: "google" | "outlook" | "apple",
email: string
) => {
const newUserData: Partial<UserProfile> = {}; const newUserData: Partial<UserProfile> = {};
if (provider === "google") { if (provider === "google") {
let googleAccounts = profileData?.googleAccounts; let googleAccounts = profileData?.googleAccounts;
@ -290,7 +321,7 @@ const CalendarSettingsPage = (props: {
newUserData.appleAccounts = appleAccounts; newUserData.appleAccounts = appleAccounts;
} }
} }
await updateUserData({newUserData}); await updateUserData({ newUserData });
}; };
let isConnectedToGoogle = false; let isConnectedToGoogle = false;
@ -326,23 +357,23 @@ const CalendarSettingsPage = (props: {
return ( return (
<ScrollView> <ScrollView>
<View marginH-30 marginB-30> <TouchableOpacity onPress={() => setPageIndex(0)}>
<TouchableOpacity onPress={() => props.setSelectedPage(0)}> <View row marginT-20 marginB-20 marginL-20 centerV>
<View row marginT-20 marginB-35 centerV>
<Ionicons <Ionicons
name="chevron-back" name="chevron-back"
size={14} size={14}
color="#979797" color="#979797"
style={{paddingBottom: 3}} style={{ paddingBottom: 3 }}
/> />
<Text <Text
style={{fontFamily: "Poppins_400Regular", fontSize: 14.71}} style={{ fontFamily: "Poppins_400Regular", fontSize: 14.71 }}
color="#979797" color="#979797"
> >
Return to main settings Return to main settings
</Text> </Text>
</View> </View>
</TouchableOpacity> </TouchableOpacity>
<View marginH-30 marginB-30>
<Text style={styles.subTitle}>Calendar settings</Text> <Text style={styles.subTitle}>Calendar settings</Text>
<View style={styles.card}> <View style={styles.card}>
<Text style={styles.cardTitle} marginB-14> <Text style={styles.cardTitle} marginB-14>
@ -352,7 +383,7 @@ const CalendarSettingsPage = (props: {
<TouchableOpacity onPress={() => handleChangeColor(colorMap.pink)}> <TouchableOpacity onPress={() => handleChangeColor(colorMap.pink)}>
<View style={styles.colorBox} backgroundColor={colorMap.pink}> <View style={styles.colorBox} backgroundColor={colorMap.pink}>
{selectedColor == colorMap.pink && ( {selectedColor == colorMap.pink && (
<AntDesign name="check" size={30} color="white"/> <AntDesign name="check" size={30} color="white" />
)} )}
</View> </View>
</TouchableOpacity> </TouchableOpacity>
@ -361,21 +392,21 @@ const CalendarSettingsPage = (props: {
> >
<View style={styles.colorBox} backgroundColor={colorMap.orange}> <View style={styles.colorBox} backgroundColor={colorMap.orange}>
{selectedColor == colorMap.orange && ( {selectedColor == colorMap.orange && (
<AntDesign name="check" size={30} color="white"/> <AntDesign name="check" size={30} color="white" />
)} )}
</View> </View>
</TouchableOpacity> </TouchableOpacity>
<TouchableOpacity onPress={() => handleChangeColor(colorMap.green)}> <TouchableOpacity onPress={() => handleChangeColor(colorMap.green)}>
<View style={styles.colorBox} backgroundColor={colorMap.green}> <View style={styles.colorBox} backgroundColor={colorMap.green}>
{selectedColor == colorMap.green && ( {selectedColor == colorMap.green && (
<AntDesign name="check" size={30} color="white"/> <AntDesign name="check" size={30} color="white" />
)} )}
</View> </View>
</TouchableOpacity> </TouchableOpacity>
<TouchableOpacity onPress={() => handleChangeColor(colorMap.teal)}> <TouchableOpacity onPress={() => handleChangeColor(colorMap.teal)}>
<View style={styles.colorBox} backgroundColor={colorMap.teal}> <View style={styles.colorBox} backgroundColor={colorMap.teal}>
{selectedColor == colorMap.teal && ( {selectedColor == colorMap.teal && (
<AntDesign name="check" size={30} color="white"/> <AntDesign name="check" size={30} color="white" />
)} )}
</View> </View>
</TouchableOpacity> </TouchableOpacity>
@ -384,7 +415,7 @@ const CalendarSettingsPage = (props: {
> >
<View style={styles.colorBox} backgroundColor={colorMap.purple}> <View style={styles.colorBox} backgroundColor={colorMap.purple}>
{selectedColor == colorMap.purple && ( {selectedColor == colorMap.purple && (
<AntDesign name="check" size={30} color="white"/> <AntDesign name="check" size={30} color="white" />
)} )}
</View> </View>
</TouchableOpacity> </TouchableOpacity>
@ -428,136 +459,173 @@ const CalendarSettingsPage = (props: {
label={"Connect Google"} label={"Connect Google"}
labelStyle={styles.addCalLbl} labelStyle={styles.addCalLbl}
labelProps={{ labelProps={{
numberOfLines: 2 numberOfLines: 2,
}} }}
iconSource={() => ( iconSource={() => (
<View marginR-15> <View marginR-15>
<GoogleIcon/> <GoogleIcon />
</View> </View>
)} )}
style={styles.addCalBtn} style={styles.addCalBtn}
color="black" color="black"
text70BL text70BL
/> />
{profileData?.googleAccounts ? Object.keys(profileData?.googleAccounts)?.map((googleMail) => { {profileData?.googleAccounts
? Object.keys(profileData?.googleAccounts)?.map((googleMail) => {
const googleToken = profileData?.googleAccounts?.[googleMail]; const googleToken = profileData?.googleAccounts?.[googleMail];
return googleToken && <Button return (
googleToken && (
<Button
key={googleMail} key={googleMail}
onPress={() => clearToken("google", googleMail)} onPress={() => clearToken("google", googleMail)}
label={`Disconnect ${googleMail}`} label={`Disconnect ${googleMail}`}
labelStyle={styles.addCalLbl} labelStyle={styles.addCalLbl}
labelProps={{ labelProps={{
numberOfLines: 2 numberOfLines: 2,
}} }}
iconSource={() => ( iconSource={() => (
<View marginR-15> <View marginR-15>
<GoogleIcon/> <GoogleIcon />
</View> </View>
)} )}
style={styles.addCalBtn} style={styles.addCalBtn}
color="black" color="black"
text70BL text70BL
/> />
}) : null} )
);
})
: null}
<Button <Button
onPress={() => handleAppleSignIn()} onPress={() => handleAppleSignIn()}
label={"Connect Apple"} label={"Connect Apple"}
labelStyle={styles.addCalLbl} labelStyle={styles.addCalLbl}
labelProps={{ labelProps={{
numberOfLines: 2 numberOfLines: 2,
}} }}
iconSource={() => ( iconSource={() => (
<View marginR-15> <View marginR-15>
<AppleIcon/> <AppleIcon />
</View> </View>
)} )}
style={styles.addCalBtn} style={styles.addCalBtn}
color="black" color="black"
text70BL text70BL
/> />
{profileData?.appleAccounts ? Object.keys(profileData?.appleAccounts)?.map((appleEmail) => { {profileData?.appleAccounts
? Object.keys(profileData?.appleAccounts)?.map((appleEmail) => {
const appleToken = profileData?.appleAccounts?.[appleEmail]; const appleToken = profileData?.appleAccounts?.[appleEmail];
return appleToken && <Button return (
appleToken && (
<Button
key={appleEmail} key={appleEmail}
onPress={() => clearToken("apple", appleEmail)} onPress={() => clearToken("apple", appleEmail)}
label={`Disconnect ${appleEmail}`} label={`Disconnect ${appleEmail}`}
labelStyle={styles.addCalLbl} labelStyle={styles.addCalLbl}
labelProps={{ labelProps={{
numberOfLines: 2 numberOfLines: 2,
}} }}
iconSource={() => ( iconSource={() => (
<View marginR-15> <View marginR-15>
<AppleIcon/> <AppleIcon />
</View> </View>
)} )}
style={styles.addCalBtn} style={styles.addCalBtn}
color="black" color="black"
text70BL text70BL
/> />
}) : null} )
);
})
: null}
<Button <Button
onPress={() => handleMicrosoftSignIn()} onPress={() => handleMicrosoftSignIn()}
label={"Connect Outlook"} label={"Connect Outlook"}
labelStyle={styles.addCalLbl} labelStyle={styles.addCalLbl}
labelProps={{ labelProps={{
numberOfLines: 2 numberOfLines: 2,
}} }}
iconSource={() => ( iconSource={() => (
<View marginR-15> <View marginR-15>
<OutlookIcon/> <OutlookIcon />
</View> </View>
)} )}
style={styles.addCalBtn} style={styles.addCalBtn}
color="black" color="black"
text70BL text70BL
/> />
{profileData?.microsoftAccounts ? Object.keys(profileData?.microsoftAccounts)?.map((microsoftEmail) => { {profileData?.microsoftAccounts
const microsoftToken = profileData?.microsoftAccounts?.[microsoftEmail]; ? Object.keys(profileData?.microsoftAccounts)?.map(
return microsoftToken && <Button (microsoftEmail) => {
const microsoftToken =
profileData?.microsoftAccounts?.[microsoftEmail];
return (
microsoftToken && (
<Button
key={microsoftEmail} key={microsoftEmail}
onPress={() => clearToken("outlook", microsoftEmail)} onPress={() => clearToken("outlook", microsoftEmail)}
label={`Disconnect ${microsoftEmail}`} label={`Disconnect ${microsoftEmail}`}
labelStyle={styles.addCalLbl} labelStyle={styles.addCalLbl}
labelProps={{ labelProps={{
numberOfLines: 2 numberOfLines: 2,
}} }}
iconSource={() => ( iconSource={() => (
<View marginR-15> <View marginR-15>
<OutlookIcon/> <OutlookIcon />
</View> </View>
)} )}
style={styles.addCalBtn} style={styles.addCalBtn}
color="black" color="black"
text70BL text70BL
/> />
}) : null} )
);
}
)
: null}
{(isConnectedToGoogle || isConnectedToMicrosoft || isConnectedToApple) && ( {(isConnectedToGoogle ||
isConnectedToMicrosoft ||
isConnectedToApple) && (
<> <>
<Text style={styles.subTitle} marginT-30 marginB-20> <Text style={styles.subTitle} marginT-30 marginB-20>
Connected Calendars Connected Calendars
</Text> </Text>
<View style={styles.noPaddingCard}> <View style={styles.noPaddingCard}>
<View style={{marginTop: 20}}> <View style={{ marginTop: 20 }}>
{profileData?.googleAccounts && Object.keys(profileData?.googleAccounts)?.map((googleEmail) => { {profileData?.googleAccounts &&
const googleToken = profileData?.googleAccounts?.[googleEmail]; Object.keys(profileData?.googleAccounts)?.map(
return googleToken && ( (googleEmail) => {
const googleToken =
profileData?.googleAccounts?.[googleEmail];
return (
googleToken && (
<TouchableOpacity <TouchableOpacity
onPress={() => fetchAndSaveGoogleEvents({token: googleToken, email: googleEmail})} onPress={() =>
fetchAndSaveGoogleEvents({
token: googleToken,
email: googleEmail,
})
}
> >
<View row paddingR-20 center> <View row paddingR-20 center>
<Button <Button
disabled={isSyncingGoogle} disabled={isSyncingGoogle}
onPress={() => fetchAndSaveGoogleEvents({token: googleToken, email: googleEmail})} onPress={() =>
fetchAndSaveGoogleEvents({
token: googleToken,
email: googleEmail,
})
}
label={`Sync ${googleEmail}`} label={`Sync ${googleEmail}`}
labelStyle={styles.addCalLbl} labelStyle={styles.addCalLbl}
labelProps={{numberOfLines: 3}} labelProps={{ numberOfLines: 3 }}
iconSource={() => ( iconSource={() => (
<View marginR-15> <View marginR-15>
<GoogleIcon/> <GoogleIcon />
</View> </View>
)} )}
style={styles.addCalBtn} style={styles.addCalBtn}
@ -566,36 +634,49 @@ const CalendarSettingsPage = (props: {
/> />
{isSyncingGoogle ? ( {isSyncingGoogle ? (
<ActivityIndicator/> <ActivityIndicator />
) : ( ) : (
<Ionicons name={"refresh"} size={20} color={"#000000"}/> <Ionicons
name={"refresh"}
size={20}
color={"#000000"}
/>
)} )}
</View> </View>
</TouchableOpacity> </TouchableOpacity>
) )
})} );
}
)}
{profileData?.appleAccounts && Object.keys(profileData?.appleAccounts)?.map((appleEmail) => { {profileData?.appleAccounts &&
Object.keys(profileData?.appleAccounts)?.map((appleEmail) => {
const appleToken = profileData?.appleAccounts?.[appleEmail]; const appleToken = profileData?.appleAccounts?.[appleEmail];
return appleToken && ( return (
appleToken && (
<TouchableOpacity <TouchableOpacity
onPress={() => fetchAndSaveAppleEvents({ onPress={() =>
fetchAndSaveAppleEvents({
email: appleEmail, email: appleEmail,
token: appleToken token: appleToken,
})}> })
}
>
<View row paddingR-20 center> <View row paddingR-20 center>
<Button <Button
disabled={isSyncingApple} disabled={isSyncingApple}
onPress={() => fetchAndSaveAppleEvents({ onPress={() =>
fetchAndSaveAppleEvents({
email: appleEmail, email: appleEmail,
token: appleToken token: appleToken,
})} })
}
label={`Sync ${appleEmail}`} label={`Sync ${appleEmail}`}
labelStyle={styles.addCalLbl} labelStyle={styles.addCalLbl}
labelProps={{numberOfLines: 3}} labelProps={{ numberOfLines: 3 }}
iconSource={() => ( iconSource={() => (
<View marginR-15> <View marginR-15>
<AppleIcon/> <AppleIcon />
</View> </View>
)} )}
style={styles.addCalBtn} style={styles.addCalBtn}
@ -603,37 +684,50 @@ const CalendarSettingsPage = (props: {
text70BL text70BL
/> />
{isSyncingApple ? ( {isSyncingApple ? (
<ActivityIndicator/> <ActivityIndicator />
) : ( ) : (
<Ionicons name={"refresh"} size={20} color={"#000000"}/> <Ionicons
name={"refresh"}
size={20}
color={"#000000"}
/>
)} )}
</View> </View>
</TouchableOpacity> </TouchableOpacity>
) )
);
})} })}
{profileData?.microsoftAccounts && Object.keys(profileData?.microsoftAccounts)?.map((microsoftEmail) => { {profileData?.microsoftAccounts &&
const microsoftToken = profileData?.microsoftAccounts?.[microsoftEmail]; Object.keys(profileData?.microsoftAccounts)?.map(
return microsoftToken && ( (microsoftEmail) => {
const microsoftToken =
profileData?.microsoftAccounts?.[microsoftEmail];
return (
microsoftToken && (
<TouchableOpacity <TouchableOpacity
onPress={() => fetchAndSaveOutlookEvents({ onPress={() =>
fetchAndSaveOutlookEvents({
token: microsoftToken, token: microsoftToken,
email: microsoftEmail email: microsoftEmail,
})} })
}
> >
<View row paddingR-20 center> <View row paddingR-20 center>
<Button <Button
disabled={isSyncingOutlook} disabled={isSyncingOutlook}
onPress={() => fetchAndSaveOutlookEvents({ onPress={() =>
fetchAndSaveOutlookEvents({
token: microsoftToken, token: microsoftToken,
email: microsoftEmail email: microsoftEmail,
})} })
}
label={`Sync ${microsoftEmail}`} label={`Sync ${microsoftEmail}`}
labelStyle={styles.addCalLbl} labelStyle={styles.addCalLbl}
labelProps={{numberOfLines: 3}} labelProps={{ numberOfLines: 3 }}
iconSource={() => ( iconSource={() => (
<View marginR-15> <View marginR-15>
<OutlookIcon/> <OutlookIcon />
</View> </View>
)} )}
style={styles.addCalBtn} style={styles.addCalBtn}
@ -641,14 +735,20 @@ const CalendarSettingsPage = (props: {
text70BL text70BL
/> />
{isSyncingOutlook ? ( {isSyncingOutlook ? (
<ActivityIndicator/> <ActivityIndicator />
) : ( ) : (
<Ionicons name={"refresh"} size={20} color={"#000000"}/> <Ionicons
name={"refresh"}
size={20}
color={"#000000"}
/>
)} )}
</View> </View>
</TouchableOpacity> </TouchableOpacity>
) )
})} );
}
)}
</View> </View>
</View> </View>
</> </>
@ -701,7 +801,7 @@ const styles = StyleSheet.create({
width: "75%", width: "75%",
textAlign: "left", textAlign: "left",
lineHeight: 20, lineHeight: 20,
overflow: "visible" overflow: "visible",
}, },
subTitle: { subTitle: {
fontFamily: "Manrope_600SemiBold", fontFamily: "Manrope_600SemiBold",

View File

@ -4,16 +4,16 @@ import { Ionicons } from "@expo/vector-icons";
import { ToDosContextProvider } from "@/contexts/ToDosContext"; import { ToDosContextProvider } from "@/contexts/ToDosContext";
import ToDosList from "../todos/ToDosList"; import ToDosList from "../todos/ToDosList";
import { ScrollView } from "react-native-gesture-handler"; import { ScrollView } from "react-native-gesture-handler";
import { settingsPageIndex } from "../calendar/atoms";
import { useAtom } from "jotai";
const ChoreRewardSettings = () => {
const [pageIndex, setPageIndex] = useAtom(settingsPageIndex);
const ChoreRewardSettings = (props: {
setSelectedPage: (page: number) => void;
}) => {
return ( return (
<ToDosContextProvider> <ToDosContextProvider>
<View marginT-10 marginH-20> <TouchableOpacity onPress={() => setPageIndex(0)}>
<ScrollView> <View row marginT-20 marginB-20 marginL-20 centerV>
<TouchableOpacity onPress={() => props.setSelectedPage(0)}>
<View row marginT-20 marginB-35 centerV>
<Ionicons <Ionicons
name="chevron-back" name="chevron-back"
size={14} size={14}
@ -28,6 +28,8 @@ const ChoreRewardSettings = (props: {
</Text> </Text>
</View> </View>
</TouchableOpacity> </TouchableOpacity>
<View marginH-20>
<ScrollView>
<Text text60R marginB-20> <Text text60R marginB-20>
Chore Reward Settings Chore Reward Settings
</Text> </Text>

View File

@ -1,7 +1,7 @@
import {Button, Text, View} from "react-native-ui-lib"; import { Button, Text, View } from "react-native-ui-lib";
import React, {useState} from "react"; import React, { useState } from "react";
import {StyleSheet} from "react-native"; import { StyleSheet } from "react-native";
import {Octicons} from "@expo/vector-icons"; import { Octicons } from "@expo/vector-icons";
import CalendarSettingsPage from "./CalendarSettingsPage"; import CalendarSettingsPage from "./CalendarSettingsPage";
import ChoreRewardSettings from "./ChoreRewardSettings"; import ChoreRewardSettings from "./ChoreRewardSettings";
import UserSettings from "./UserSettings"; import UserSettings from "./UserSettings";
@ -9,7 +9,9 @@ import ProfileIcon from "@/assets/svgs/ProfileIcon";
import CalendarIcon from "@/assets/svgs/CalendarIcon"; import CalendarIcon from "@/assets/svgs/CalendarIcon";
import PrivacyPolicyIcon from "@/assets/svgs/PrivacyPolicyIcon"; import PrivacyPolicyIcon from "@/assets/svgs/PrivacyPolicyIcon";
import ArrowRightIcon from "@/assets/svgs/ArrowRightIcon"; import ArrowRightIcon from "@/assets/svgs/ArrowRightIcon";
import {ProfileType, useAuthContext} from "@/contexts/AuthContext"; import { ProfileType, useAuthContext } from "@/contexts/AuthContext";
import { settingsPageIndex } from "../calendar/atoms";
import { useAtom } from "jotai";
const pageIndex = { const pageIndex = {
main: 0, main: 0,
@ -20,13 +22,13 @@ const pageIndex = {
}; };
const SettingsPage = () => { const SettingsPage = () => {
const {profileData} = useAuthContext() const { profileData } = useAuthContext();
const isntParent = profileData?.userType !== ProfileType.PARENT const [pageIndex, setPageIndex] = useAtom(settingsPageIndex);
const isntParent = profileData?.userType !== ProfileType.PARENT;
const [selectedPage, setSelectedPage] = useState<number>(0);
return ( return (
<View flexG> <View flexG>
{selectedPage == 0 && ( {pageIndex == 0 && (
<View flexG centerH marginH-30 marginT-30> <View flexG centerH marginH-30 marginT-30>
<Button <Button
disabled={isntParent} disabled={isntParent}
@ -34,14 +36,19 @@ const SettingsPage = () => {
style={styles.mainBtn} style={styles.mainBtn}
children={ children={
<View row centerV width={"100%"}> <View row centerV width={"100%"}>
<ProfileIcon style={{marginRight: 10}} color="#07b9c8"/> <ProfileIcon style={{ marginRight: 10 }} color="#07b9c8" />
<Text style={[styles.label, isntParent && styles.disabledText]}> <Text
style={[
styles.label,
isntParent ? styles.disabledText : { color: "#07b9c8" },
]}
>
Manage My Profile Manage My Profile
</Text> </Text>
<ArrowRightIcon style={{marginLeft: "auto"}}/> <ArrowRightIcon style={{ marginLeft: "auto" }} />
</View> </View>
} }
onPress={() => setSelectedPage(pageIndex.user)} onPress={() => setPageIndex(1)}
/> />
<Button <Button
disabled={isntParent} disabled={isntParent}
@ -49,15 +56,20 @@ const SettingsPage = () => {
style={styles.mainBtn} style={styles.mainBtn}
children={ children={
<View row centerV width={"100%"}> <View row centerV width={"100%"}>
<CalendarIcon style={{marginRight: 10}}/> <CalendarIcon style={{ marginRight: 10 }} />
<Text style={[styles.label, isntParent && styles.disabledText]}> <Text
style={[
styles.label,
isntParent ? styles.disabledText : { color: "#FD1775" },
]}
>
Calendar Settings Calendar Settings
</Text> </Text>
<ArrowRightIcon style={{marginLeft: "auto"}}/> <ArrowRightIcon style={{ marginLeft: "auto" }} />
</View> </View>
} }
onPress={() => { onPress={() => {
setSelectedPage(pageIndex.calendar); setPageIndex(2);
}} }}
/> />
<Button <Button
@ -70,40 +82,32 @@ const SettingsPage = () => {
name="gear" name="gear"
size={24} size={24}
color="#ff9900" color="#ff9900"
style={{marginRight: 10}} style={{ marginRight: 10 }}
/> />
<Text style={[styles.label, isntParent && styles.disabledText]}> <Text style={[styles.label, isntParent ? styles.disabledText : {color: "#ff9900"}]}>
To-Do Reward Settings To-Do Reward Settings
</Text> </Text>
<ArrowRightIcon style={{marginLeft: "auto"}}/> <ArrowRightIcon style={{ marginLeft: "auto" }} />
</View> </View>
} }
onPress={() => setSelectedPage(pageIndex.chore)} onPress={() => setPageIndex(3)}
/> />
<Button <Button
backgroundColor="white" backgroundColor="white"
style={styles.mainBtn} style={styles.mainBtn}
children={ children={
<View row centerV width={"100%"}> <View row centerV width={"100%"}>
<PrivacyPolicyIcon style={{marginRight: 10}}/> <PrivacyPolicyIcon style={{ marginRight: 10 }} />
<Text style={styles.label}> <Text style={[styles.label]} color={"#6C645B"}>Cally Privacy Policy</Text>
Cally Privacy Policy <ArrowRightIcon style={{ marginLeft: "auto" }} />
</Text>
<ArrowRightIcon style={{marginLeft: "auto"}}/>
</View> </View>
} }
/> />
</View> </View>
)} )}
{selectedPage == pageIndex.calendar && ( {pageIndex == 2 && <CalendarSettingsPage />}
<CalendarSettingsPage setSelectedPage={setSelectedPage}/> {pageIndex == 3 && <ChoreRewardSettings />}
)} {pageIndex == 1 && <UserSettings />}
{selectedPage == pageIndex.chore && (
<ChoreRewardSettings setSelectedPage={setSelectedPage}/>
)}
{selectedPage == pageIndex.user && (
<UserSettings setSelectedPage={setSelectedPage}/>
)}
</View> </View>
); );
}; };
@ -123,6 +127,6 @@ const styles = StyleSheet.create({
textAlignVertical: "center", textAlignVertical: "center",
}, },
disabledText: { disabledText: {
color: '#A9A9A9', // Example of a gray color for disabled text color: "#A9A9A9", // Example of a gray color for disabled text
}, },
}); });

View File

@ -4,15 +4,30 @@ import { Ionicons } from "@expo/vector-icons";
import { ScrollView, StyleSheet } from "react-native"; import { ScrollView, StyleSheet } from "react-native";
import MyProfile from "./user_settings_views/MyProfile"; import MyProfile from "./user_settings_views/MyProfile";
import MyGroup from "./user_settings_views/MyGroup"; import MyGroup from "./user_settings_views/MyGroup";
import { useAtom } from "jotai";
import { settingsPageIndex, userSettingsView } from "../calendar/atoms";
import { AuthContextProvider } from "@/contexts/AuthContext";
const UserSettings = (props: { setSelectedPage: (page: number) => void }) => { const UserSettings = () => {
const [selectedView, setSelectedView] = useState<boolean>(true); const [pageIndex, setPageIndex] = useAtom(settingsPageIndex);
const [userView, setUserView] = useAtom(userSettingsView);
return ( return (
<AuthContextProvider>
<View flexG> <View flexG>
<ScrollView style={{ paddingBottom: 20, minHeight: "100%" }}> <ScrollView style={{ paddingBottom: 20, minHeight: "100%" }}>
<TouchableOpacity onPress={() => props.setSelectedPage(0)}> <TouchableOpacity
<View row marginT-20 marginB-35 centerV> onPress={() => {
<Ionicons name="chevron-back" size={14} color="#979797" style={{paddingBottom: 3}} /> setPageIndex(0);
setUserView(true);
}}
>
<View row marginT-20 marginB-20 marginL-20 centerV>
<Ionicons
name="chevron-back"
size={14}
color="#979797"
style={{ paddingBottom: 3 }}
/>
<Text <Text
style={{ fontFamily: "Poppins_400Regular", fontSize: 14.71 }} style={{ fontFamily: "Poppins_400Regular", fontSize: 14.71 }}
color="#979797" color="#979797"
@ -21,53 +36,54 @@ const UserSettings = (props: { setSelectedPage: (page: number) => void }) => {
</Text> </Text>
</View> </View>
</TouchableOpacity> </TouchableOpacity>
<View marginH-20 flexG style={{ minHeight: "90%" }}> <View marginH-26 flexG style={{ minHeight: "90%" }}>
<Text text60R marginB-25> <Text text60R marginB-25>
User Management User Management
</Text> </Text>
<View style={styles.buttonSwitch} spread row> <View style={styles.buttonSwitch} spread row>
<TouchableOpacity <TouchableOpacity
onPress={() => setSelectedView(true)} onPress={() => setUserView(true)}
centerV centerV
centerH centerH
style={selectedView == true ? styles.btnSelected : styles.btnNot} style={userView == true ? styles.btnSelected : styles.btnNot}
> >
<View> <View>
<Text <Text
style={styles.btnTxt} style={styles.btnTxt}
color={selectedView ? "white" : "black"} color={userView ? "white" : "black"}
> >
My Profile My Profile
</Text> </Text>
</View> </View>
</TouchableOpacity> </TouchableOpacity>
<TouchableOpacity <TouchableOpacity
onPress={() => setSelectedView(false)} onPress={() => setUserView(false)}
centerV centerV
centerH centerH
style={selectedView == false ? styles.btnSelected : styles.btnNot} style={userView == false ? styles.btnSelected : styles.btnNot}
> >
<View> <View>
<Text <Text
style={styles.btnTxt} style={styles.btnTxt}
color={!selectedView ? "white" : "black"} color={!userView ? "white" : "black"}
> >
My Group My Group
</Text> </Text>
</View> </View>
</TouchableOpacity> </TouchableOpacity>
</View> </View>
{selectedView && <MyProfile />} {userView && <MyProfile />}
{!selectedView && <MyGroup />} {!userView && <MyGroup />}
</View> </View>
</ScrollView> </ScrollView>
{!selectedView && ( {!userView && (
<View> <View>
<Text>selview</Text> <Text>selview</Text>
</View> </View>
)} )}
</View> </View>
</AuthContextProvider>
); );
}; };

View File

@ -14,21 +14,21 @@ import {
TouchableOpacity, TouchableOpacity,
View, View,
} from "react-native-ui-lib"; } from "react-native-ui-lib";
import React, {useEffect, useRef, useState} from "react"; import React, { useEffect, useRef, useState } from "react";
import {ScrollView, StyleSheet} from "react-native"; import { ImageBackground, ScrollView, StyleSheet } from "react-native";
import {PickerSingleValue} from "react-native-ui-lib/src/components/picker/types"; import { PickerSingleValue } from "react-native-ui-lib/src/components/picker/types";
import {useCreateSubUser} from "@/hooks/firebase/useCreateSubUser"; import { useCreateSubUser } from "@/hooks/firebase/useCreateSubUser";
import {ProfileType} from "@/contexts/AuthContext"; import { ProfileType, useAuthContext } from "@/contexts/AuthContext";
import {useGetFamilyMembers} from "@/hooks/firebase/useGetFamilyMembers"; import { useGetFamilyMembers } from "@/hooks/firebase/useGetFamilyMembers";
import UserMenu from "@/components/pages/settings/user_settings_views/UserMenu"; import UserMenu from "@/components/pages/settings/user_settings_views/UserMenu";
import {uuidv4} from "@firebase/util"; import { uuidv4 } from "@firebase/util";
import QRIcon from "@/assets/svgs/QRIcon"; import QRIcon from "@/assets/svgs/QRIcon";
import EmailIcon from "@/assets/svgs/EmailIcon"; import EmailIcon from "@/assets/svgs/EmailIcon";
import CircledXIcon from "@/assets/svgs/CircledXIcon"; import CircledXIcon from "@/assets/svgs/CircledXIcon";
import ProfileIcon from "@/assets/svgs/ProfileIcon"; import ProfileIcon from "@/assets/svgs/ProfileIcon";
import NavToDosIcon from "@/assets/svgs/NavToDosIcon"; import NavToDosIcon from "@/assets/svgs/NavToDosIcon";
import Ionicons from "@expo/vector-icons/Ionicons"; import Ionicons from "@expo/vector-icons/Ionicons";
import {PreviousNextView} from "react-native-keyboard-manager"; import { PreviousNextView } from "react-native-keyboard-manager";
const MyGroup = () => { const MyGroup = () => {
const [showAddUserDialog, setShowAddUserDialog] = useState(false); const [showAddUserDialog, setShowAddUserDialog] = useState(false);
@ -43,10 +43,13 @@ const MyGroup = () => {
const lNameRef = useRef<TextFieldRef>(null); const lNameRef = useRef<TextFieldRef>(null);
const emailRef = useRef<TextFieldRef>(null); const emailRef = useRef<TextFieldRef>(null);
const [showQRCodeDialog, setShowQRCodeDialog] = useState<string | boolean>(false); const [showQRCodeDialog, setShowQRCodeDialog] = useState<string | boolean>(
false
);
const {mutateAsync: createSubUser, isLoading, isError} = useCreateSubUser(); const { mutateAsync: createSubUser, isLoading, isError } = useCreateSubUser();
const {data: familyMembers} = useGetFamilyMembers(true); const { data: familyMembers } = useGetFamilyMembers(true);
const { user } = useAuthContext();
const parents = const parents =
familyMembers?.filter((x) => x.userType === ProfileType.PARENT) ?? []; familyMembers?.filter((x) => x.userType === ProfileType.PARENT) ?? [];
@ -101,12 +104,11 @@ const MyGroup = () => {
setFirstName(""); setFirstName("");
setLastName(""); setLastName("");
setEmail(""); setEmail("");
}, []) }, []);
// @ts-ignore // @ts-ignore
return ( return (
<View style={{flex: 1, minHeight: 500}}> <View style={{ flex: 1, minHeight: 500 }}>
<View> <View>
<ScrollView style={styles.card}> <ScrollView style={styles.card}>
{!parents.length && !children.length && !caregivers.length && ( {!parents.length && !children.length && !caregivers.length && (
@ -117,7 +119,7 @@ const MyGroup = () => {
{(!!parents.length || !!children.length) && ( {(!!parents.length || !!children.length) && (
<> <>
<Text style={styles.subTit} marginV-10> <Text style={styles.subTit} marginB-10>
Family Family
</Text> </Text>
{[...parents, ...children]?.map((member, index) => ( {[...parents, ...children]?.map((member, index) => (
@ -128,31 +130,37 @@ const MyGroup = () => {
style={styles.familyCard} style={styles.familyCard}
row row
centerV centerV
padding-10 paddingT-10
> >
<Avatar {member.pfp ? (
source={{uri: "https://via.placeholder.com/60"}} <ImageBackground
size={40} style={styles.pfp}
backgroundColor={Colors.grey60} borderRadius={10.56}
source={{ uri: member.pfp || undefined }}
/> />
<View marginL-10> ) : (
<Text text70M> <View style={[styles.pfp, {backgroundColor: "#ea156d"}]} />
)}
<View row marginL-10 centerV>
<Text style={styles.name}>
{member.firstName} {member.lastName} {member.firstName} {member.lastName}
</Text> </Text>
<Text text90 grey40> </View>
<View flexG />
<View row centerV gap-10>
<Text style={styles.userType}>
{member.userType === ProfileType.PARENT {member.userType === ProfileType.PARENT
? "Admin (You)" ? `Admin${
member.uid === user?.uid ? " (You)" : ""
}`
: "Child"} : "Child"}
</Text> </Text>
</View>
<View flex-1/>
<UserMenu <UserMenu
setShowQRCodeDialog={(val) => setShowQRCodeDialog(val)} setShowQRCodeDialog={(val) => setShowQRCodeDialog(val)}
showQRCodeDialog={showQRCodeDialog === member?.uid} showQRCodeDialog={showQRCodeDialog === member?.uid}
userId={member?.uid!} userId={member?.uid!}
/> />
</View>
</Card> </Card>
))} ))}
</> </>
@ -160,7 +168,7 @@ const MyGroup = () => {
{!!caregivers.length && ( {!!caregivers.length && (
<> <>
<Text text70 marginB-10 marginT-15> <Text style={styles.subTit} marginB-10 marginT-15>
Caregivers Caregivers
</Text> </Text>
{caregivers?.map((member) => ( {caregivers?.map((member) => (
@ -174,7 +182,7 @@ const MyGroup = () => {
padding-10 padding-10
> >
<Avatar <Avatar
source={{uri: member?.pfp ?? undefined}} source={{ uri: member?.pfp ?? undefined }}
size={40} size={40}
backgroundColor={Colors.grey60} backgroundColor={Colors.grey60}
/> />
@ -187,7 +195,7 @@ const MyGroup = () => {
</Text> </Text>
</View> </View>
<View flex-1/> <View flex-1 />
<UserMenu <UserMenu
setShowQRCodeDialog={(val) => setShowQRCodeDialog(val)} setShowQRCodeDialog={(val) => setShowQRCodeDialog(val)}
@ -215,7 +223,7 @@ const MyGroup = () => {
padding-10 padding-10
> >
<Avatar <Avatar
source={{uri: member?.pfp ?? undefined}} source={{ uri: member?.pfp ?? undefined }}
size={40} size={40}
backgroundColor={Colors.grey60} backgroundColor={Colors.grey60}
/> />
@ -226,7 +234,7 @@ const MyGroup = () => {
</Text> </Text>
</View> </View>
<View flex-1/> <View flex-1 />
<UserMenu <UserMenu
setShowQRCodeDialog={(val) => setShowQRCodeDialog(val)} setShowQRCodeDialog={(val) => setShowQRCodeDialog(val)}
@ -269,7 +277,7 @@ const MyGroup = () => {
</Text> </Text>
<Button backgroundColor={"#FD1775"} style={styles.dialogBtn}> <Button backgroundColor={"#FD1775"} style={styles.dialogBtn}>
<QRIcon/> <QRIcon />
<Text style={styles.dialogBtnLbl} marginL-7> <Text style={styles.dialogBtnLbl} marginL-7>
Show a QR Code Show a QR Code
</Text> </Text>
@ -284,7 +292,7 @@ const MyGroup = () => {
}, 500); }, 500);
}} }}
> >
<EmailIcon/> <EmailIcon />
<Text style={styles.dialogBtnLbl} marginL-7> <Text style={styles.dialogBtnLbl} marginL-7>
Enter email address Enter email address
</Text> </Text>
@ -309,30 +317,31 @@ const MyGroup = () => {
<KeyboardAwareScrollView> <KeyboardAwareScrollView>
<Card padding-25 style={styles.dialogCard}> <Card padding-25 style={styles.dialogCard}>
<View row spread> <View row spread>
<Text style={{fontFamily: "Manrope_500Medium", fontSize: 16}}> <Text style={{ fontFamily: "Manrope_500Medium", fontSize: 16 }}>
New User Information New User Information
</Text> </Text>
<TouchableOpacity onPress={() => { <TouchableOpacity
setShowNewUserInfoDialog(false) onPress={() => {
}}> setShowNewUserInfoDialog(false);
<CircledXIcon/> }}
>
<CircledXIcon />
</TouchableOpacity> </TouchableOpacity>
</View> </View>
<View style={styles.divider} spread/> <View style={styles.divider} spread />
<View row centerV gap-20 marginV-20> <View row centerV gap-20 marginV-20>
<View <View
height={65.54} height={65.54}
width={65.54} width={65.54}
children={ children={
<ProfileIcon color={"#d6d6d6"} width={37} height={37}/> <ProfileIcon color={"#d6d6d6"} width={37} height={37} />
} }
backgroundColor={Colors.grey60} backgroundColor={Colors.grey60}
style={{borderRadius: 25}} style={{ borderRadius: 25 }}
center center
/> />
<TouchableOpacity onPress={() => { <TouchableOpacity onPress={() => {}}>
}}>
<Text color="#50be0c" style={styles.jakarta13} marginL-15> <Text color="#50be0c" style={styles.jakarta13} marginL-15>
Upload User Profile Photo Upload User Profile Photo
</Text> </Text>
@ -349,21 +358,30 @@ const MyGroup = () => {
floatingPlaceholder floatingPlaceholder
style={styles.inViewPicker} style={styles.inViewPicker}
trailingAccessory={ trailingAccessory={
<View style={{ <View
style={{
justifyContent: "center", justifyContent: "center",
alignItems: "center", alignItems: "center",
height: "100%", height: "100%",
marginTop: -38, marginTop: -38,
paddingRight: 15 paddingRight: 15,
}}> }}
<Ionicons name={"chevron-down"} style={{alignSelf: "center"}} size={20} >
color={"#000000"}/> <Ionicons
name={"chevron-down"}
style={{ alignSelf: "center" }}
size={20}
color={"#000000"}
/>
</View> </View>
} }
> >
<Picker.Item label="Child" value={ProfileType.CHILD}/> <Picker.Item label="Child" value={ProfileType.CHILD} />
<Picker.Item label="Parent" value={ProfileType.PARENT}/> <Picker.Item label="Parent" value={ProfileType.PARENT} />
<Picker.Item label="Caregiver" value={ProfileType.CAREGIVER}/> <Picker.Item
label="Caregiver"
value={ProfileType.CAREGIVER}
/>
<Picker.Item <Picker.Item
label="Family Device" label="Family Device"
value={ProfileType.FAMILY_DEVICE} value={ProfileType.FAMILY_DEVICE}
@ -387,7 +405,7 @@ const MyGroup = () => {
onChangeText={setFirstName} onChangeText={setFirstName}
style={styles.inputField} style={styles.inputField}
onSubmitEditing={() => { onSubmitEditing={() => {
lNameRef.current?.focus() lNameRef.current?.focus();
}} }}
blurOnSubmit={false} blurOnSubmit={false}
returnKeyType="next" returnKeyType="next"
@ -404,11 +422,10 @@ const MyGroup = () => {
onChangeText={setLastName} onChangeText={setLastName}
style={styles.inputField} style={styles.inputField}
onSubmitEditing={() => { onSubmitEditing={() => {
emailRef.current?.focus() emailRef.current?.focus();
}} }}
blurOnSubmit={false} blurOnSubmit={false}
returnKeyType="next" returnKeyType="next"
/> />
</> </>
)} )}
@ -443,8 +460,8 @@ const MyGroup = () => {
fontSize: 15, fontSize: 15,
marginLeft: 7, marginLeft: 7,
}} }}
style={{marginTop: 20, backgroundColor: "#fd1775"}} style={{ marginTop: 20, backgroundColor: "#fd1775" }}
iconSource={() => <NavToDosIcon width={22} color={"white"}/>} iconSource={() => <NavToDosIcon width={22} color={"white"} />}
onPress={handleCreateSubUser} onPress={handleCreateSubUser}
/> />
</Card> </Card>
@ -460,7 +477,7 @@ const styles = StyleSheet.create({
height: 47, height: 47,
width: 279, width: 279,
}, },
dialogTitle: {fontFamily: "Manrope_600SemiBold", fontSize: 22}, dialogTitle: { fontFamily: "Manrope_600SemiBold", fontSize: 22 },
dialogBackBtn: { dialogBackBtn: {
fontFamily: "PlusJakartaSans_500Medium", fontFamily: "PlusJakartaSans_500Medium",
fontSize: 15, fontSize: 15,
@ -470,8 +487,9 @@ const styles = StyleSheet.create({
marginVertical: 15, marginVertical: 15,
backgroundColor: "white", backgroundColor: "white",
width: "100%", width: "100%",
borderRadius: 15, borderRadius: 12,
padding: 20, paddingHorizontal: 21,
paddingVertical: 20,
}, },
bottomButton: { bottomButton: {
position: "absolute", position: "absolute",
@ -546,7 +564,7 @@ const styles = StyleSheet.create({
fontSize: 15, fontSize: 15,
color: "white", color: "white",
}, },
divider: {height: 0.7, backgroundColor: "#e6e6e6", width: "100%"}, divider: { height: 0.7, backgroundColor: "#e6e6e6", width: "100%" },
jakarta12: { jakarta12: {
fontFamily: "PlusJakartaSans_500Medium", fontFamily: "PlusJakartaSans_500Medium",
fontSize: 12, fontSize: 12,
@ -556,6 +574,16 @@ const styles = StyleSheet.create({
fontFamily: "PlusJakartaSans_500Medium", fontFamily: "PlusJakartaSans_500Medium",
fontSize: 13, fontSize: 13,
}, },
pfp: { aspectRatio: 1, width: 37.03, borderRadius: 10.56 },
userType: {
fontFamily: "Manrope_500Medium",
fontSize: 12,
color: "#858585",
},
name: {
fontFamily: "Manrope_600SemiBold",
fontSize: 16,
},
}); });
export default MyGroup; export default MyGroup;