add braindump

This commit is contained in:
ivic00
2024-10-11 02:00:11 +02:00
parent ca0b55c494
commit e1c03c840d
11 changed files with 605 additions and 395 deletions

View File

@ -91,6 +91,9 @@ android {
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 1
versionName "1.0.0"
manifestPlaceholders = [
appAuthRedirectScheme: "callyplanner"
]
}
signingConfigs {
debug {

View File

@ -20,11 +20,11 @@ SplashScreen.preventAutoHideAsync();
const queryClient = new QueryClient()
// if (__DEV__) {
// functions().useEmulator('localhost', 5001);
// firestore().useEmulator("localhost", 5471);
// auth().useEmulator("http://localhost:9099");
// }
if (__DEV__) {
functions().useEmulator('localhost', 5001);
firestore().useEmulator("localhost", 5471);
auth().useEmulator("http://localhost:9099");
}
export default function RootLayout() {
const [loaded] = useFonts({

21
assets/svgs/BinIcon.tsx Normal file
View File

@ -0,0 +1,21 @@
import * as React from "react"
import Svg, { Path } from "react-native-svg"
interface BinIconProps extends React.SVGProps<SVGSVGElement> {}
const BinIcon: React.FC<BinIconProps> = (props) => (
<Svg
width={19}
height={21}
fill="none"
{...props}
>
<Path
stroke="#B7B7B7"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={1.7}
d="m15.568 4.122-.834 12.5c-.072 1.095-.11 1.642-.345 2.057-.209.366-.523.66-.901.843-.43.208-.979.208-2.076.208H7.237c-1.097 0-1.646 0-2.076-.208a2.081 2.081 0 0 1-.9-.843c-.237-.415-.274-.962-.347-2.057l-.833-12.5M1 4.122h16.649m-4.162 0-.282-.845c-.273-.819-.41-1.228-.662-1.53a2.08 2.08 0 0 0-.835-.603C11.34 1 10.909 1 10.046 1H8.603c-.863 0-1.295 0-1.662.144a2.081 2.081 0 0 0-.835.602c-.253.303-.39.712-.662 1.53l-.282.846m6.244 4.162v7.284M7.243 8.284v7.284"
/>
</Svg>
)
export default BinIcon

20
assets/svgs/PenIcon.tsx Normal file
View File

@ -0,0 +1,20 @@
import * as React from "react";
import Svg, { Path } from "react-native-svg";
interface PenIconProps extends React.SVGProps<SVGSVGElement> {}
const PenIcon: React.FC<PenIconProps> = (props) => (
<Svg
xmlns="http://www.w3.org/2000/svg"
width={17}
height={19}
fill="none"
{...props}
>
<Path
fill="#919191"
d="M9.46 4.489a.907.907 0 1 0-1.416-1.135l1.417 1.135Zm-7.844 8.337.687.592a.835.835 0 0 0 .021-.025l-.708-.567Zm-.207.505-.906-.059v.017l.906.042Zm-.17 3.658-.906-.042c-.002.045 0 .09.004.135l.903-.093Zm.953.831.03.907c.06-.002.12-.01.18-.024l-.21-.883Zm3.63-.86.21.883.013-.003-.223-.88Zm.466-.295.7.578.008-.01-.708-.568Zm8.016-8.546a.908.908 0 0 0-1.416-1.136l1.416 1.136ZM8.048 3.353A.908.908 0 0 0 9.464 4.49L8.048 3.353Zm2.63-1.828.707.568a.942.942 0 0 0 .035-.046l-.742-.522Zm1.97-.387.581-.697a.915.915 0 0 0-.09-.067l-.49.764Zm2.707 2.253.643-.64a.941.941 0 0 0-.062-.057l-.58.697Zm-.01 1.982-.636-.648a.92.92 0 0 0-.072.08l.707.568Zm-2.457 1.61a.907.907 0 1 0 1.416 1.136l-1.416-1.136ZM9.654 3.787a.907.907 0 1 0-1.795.269l1.795-.27Zm4.064 4.663a.908.908 0 0 0-.244-1.798l.244 1.798ZM8.044 3.354.908 12.258l1.416 1.135L9.461 4.49 8.044 3.354ZM.93 12.233c-.251.291-.4.656-.426 1.04l1.812.117c-.001.01-.005.02-.012.028L.93 12.233Zm-.427 1.056-.169 3.658 1.813.084.17-3.658-1.814-.084Zm-.165 3.793a1.834 1.834 0 0 0 1.884 1.645l-.059-1.814h-.007a.02.02 0 0 1-.006-.004.02.02 0 0 1-.005-.006.018.018 0 0 1-.002-.007l-1.805.186ZM2.4 18.703l3.63-.86-.419-1.766-3.63.86.42 1.766Zm3.644-.863c.37-.094.7-.303.943-.598l-1.4-1.155a.021.021 0 0 1 .01-.006l.447 1.759Zm.95-.607 7.309-9.114-1.416-1.136-7.308 9.114 1.416 1.136ZM9.465 4.489l1.921-2.396L9.97.958 8.048 3.353 9.464 4.49Zm1.956-2.442a.544.544 0 0 1 .739-.145l.98-1.528a2.36 2.36 0 0 0-3.204.63l1.485 1.043Zm.648-.211 2.707 2.253 1.161-1.395L13.229.44l-1.16 1.395Zm2.645 2.196a.487.487 0 0 1 .143.347l1.815.01a2.303 2.303 0 0 0-.673-1.639l-1.285 1.282Zm.143.347c-.001.13-.054.255-.147.346l1.27 1.296c.44-.43.688-1.017.692-1.632l-1.816-.01Zm-.22.426-1.748 2.178 1.416 1.136 1.748-2.178-1.415-1.136Zm-6.777-.75a5.215 5.215 0 0 0 5.86 4.395l-.245-1.798a3.4 3.4 0 0 1-3.82-2.865l-1.795.269Z"
/>
</Svg>
);
export default PenIcon;

View File

@ -0,0 +1,97 @@
import { View, Text, Button, TextField } from "react-native-ui-lib";
import React, { useEffect, useState } from "react";
import { Dialog } from "react-native-ui-lib";
import { PanningDirectionsEnum } from "react-native-ui-lib/src/incubator/panView";
import CloseXIcon from "@/assets/svgs/CloseXIcon";
import { StyleSheet } from "react-native";
import DropModalIcon from "@/assets/svgs/DropModalIcon";
import MenuIcon from "@/assets/svgs/MenuIcon";
import { useBrainDumpContext } from "@/contexts/DumpContext";
interface IAddBrainDumpProps {
isVisible: boolean;
setIsVisible: (value: boolean) => void;
}
const AddBrainDump = ({
addBrainDumpProps,
}: {
addBrainDumpProps: IAddBrainDumpProps;
}) => {
const {addBrainDump} = useBrainDumpContext();
const [dumpTitle, setDumpTitle] = useState<string>("");
const [dumpDesc, setDumpDesc] = useState<string>("");
useEffect(() => {
setDumpDesc("");
setDumpTitle("");
}, [addBrainDumpProps.isVisible]);
return (
<Dialog
bottom={true}
height={"90%"}
panDirection={PanningDirectionsEnum.DOWN}
onDismiss={() => addBrainDumpProps.setIsVisible(false)}
containerStyle={{
borderRadius: 10,
backgroundColor: "white",
width: "100%",
alignSelf: "stretch",
padding: 0,
paddingTop: 3,
margin: 0,
}}
visible={addBrainDumpProps.isVisible}
>
<View row spread style={styles.topBtns} marginB-20>
<Button
color="#05a8b6"
label="Cancel"
style={styles.topBtn}
onPress={() => {
addBrainDumpProps.setIsVisible(false);
}}
/>
<DropModalIcon style={{ marginTop: 15 }} onPress={() => addBrainDumpProps.setIsVisible(false)}/>
<Button
color="#05a8b6"
label="Save"
style={styles.topBtn}
onPress={() => {
addBrainDump({id: 99, title: dumpTitle, description: dumpDesc})
addBrainDumpProps.setIsVisible(false);
}}
/>
</View>
<View marginH-20>
<TextField
value={dumpTitle}
placeholder="Set Title"
text60R
onChangeText={(text) => {
setDumpTitle(text);
}}
/>
<View height={2} backgroundColor="#b3b3b3" width={"100%"} marginB-20/>
<TextField
value={dumpDesc}
placeholder="Write Description"
text70
onChangeText={(text) => {
setDumpDesc(text);
}}
/>
</View>
</Dialog>
);
};
const styles = StyleSheet.create({
topBtns: {},
topBtn: {
backgroundColor: "white",
color: "#05a8b6",
},
});
export default AddBrainDump;

View File

@ -1,15 +1,17 @@
import { ScrollView } from "react-native";
import React, { useState } from "react";
import { View, Text } from "react-native-ui-lib";
import { View, Text, Button } from "react-native-ui-lib";
import DumpList from "./DumpList";
import HeaderTemplate from "@/components/shared/HeaderTemplate";
import { TextField } from "react-native-ui-lib";
import { StyleSheet } from "react-native";
import { Feather } from "@expo/vector-icons";
import { Feather, MaterialIcons } from "@expo/vector-icons";
import { TextInput } from "react-native-gesture-handler";
import AddBrainDump from "./AddBrainDump";
const BrainDumpPage = () => {
const [searchText, setSearchText] = useState<string>("");
const [isAddVisible, setIsAddVisible] = useState<boolean>(false);
return (
<View>
@ -44,6 +46,35 @@ const BrainDumpPage = () => {
</View>
</View>
</ScrollView>
<Button
style={{
position: "absolute",
bottom: 20,
right: 20,
height: 40,
borderRadius: 30,
backgroundColor: "#fd1775",
alignItems: "center",
justifyContent: "center",
}}
centerV
color="white"
enableShadow
iconSource={() => (
<MaterialIcons name="add" size={22} color={"white"} />
)}
onPress={() => {
setIsAddVisible(true);
}}
label="New"
text60R
/>
<AddBrainDump
addBrainDumpProps={{
isVisible: isAddVisible,
setIsVisible: setIsAddVisible,
}}
/>
</View>
);
};

View File

@ -1,24 +1,31 @@
import { View } from 'react-native-ui-lib';
import React from 'react';
import { useBrainDumpContext } from '@/contexts/DumpContext';
import { FlatList } from 'react-native';
import BrainDumpItem from './DumpItem';
import { View } from "react-native-ui-lib";
import React from "react";
import { useBrainDumpContext } from "@/contexts/DumpContext";
import { FlatList } from "react-native";
import BrainDumpItem from "./DumpItem";
const DumpList = (props: { searchText: string }) => {
const { brainDumps } = useBrainDumpContext();
const filteredBrainDumps = props.searchText.trim() === ""
? brainDumps
: brainDumps.filter((item) =>
item.title.toLowerCase().includes(props.searchText.toLowerCase())
);
const filteredBrainDumps =
props.searchText.trim() === ""
? brainDumps
: brainDumps.filter(
(item) =>
item.title.toLowerCase().includes(props.searchText.toLowerCase()) ||
item.description
.toLowerCase()
.includes(props.searchText.toLowerCase())
);
return (
<View>
<FlatList
data={filteredBrainDumps}
keyExtractor={(item) => item.title}
renderItem={({ item }) => <BrainDumpItem key={item.title} item={item} />}
renderItem={({ item }) => (
<BrainDumpItem key={item.title} item={item} />
)}
/>
</View>
);

View File

@ -1,19 +1,29 @@
import React, { useState } from "react";
import React, { useEffect, useState } from "react";
import { Button, Dialog, View, Text, TextField } from "react-native-ui-lib";
import { StyleSheet } from "react-native";
import { PanningDirectionsEnum } from "react-native-ui-lib/src/incubator/panView";
import { IBrainDump } from "@/contexts/DumpContext";
import { IBrainDump, useBrainDumpContext } from "@/contexts/DumpContext";
import { Entypo, EvilIcons, Feather, Octicons } from "@expo/vector-icons";
import { TouchableOpacity } from "react-native-gesture-handler";
import PenIcon from "@/assets/svgs/PenIcon";
import BinIcon from "@/assets/svgs/BinIcon";
import DropModalIcon from "@/assets/svgs/DropModalIcon";
import CloseXIcon from "@/assets/svgs/CloseXIcon";
const MoveBrainDump = (props: {
item: IBrainDump;
isVisible: boolean;
setIsVisible: (value: boolean) => void;
}) => {
const { updateBrainDumpItem, deleteBrainDump } = useBrainDumpContext();
const [description, setDescription] = useState<string>(
props.item.description
);
useEffect(() => {
updateBrainDumpItem(props.item.id, { description: description });
}, [description]);
return (
<Dialog
bottom={true}
@ -35,16 +45,14 @@ const MoveBrainDump = (props: {
<Button
color="#05a8b6"
style={styles.topBtn}
iconSource={() => <EvilIcons name="close" size={30} color="black" />}
iconSource={() => <CloseXIcon />}
onPress={() => {
props.setIsVisible(false);
}}
/>
<Button
style={styles.topBtn}
iconSource={() => (
<Feather name="chevron-down" size={24} color="black" />
)}
iconSource={() => <DropModalIcon />}
onPress={() => {
props.setIsVisible(false);
}}
@ -52,17 +60,19 @@ const MoveBrainDump = (props: {
<View row>
<Button
style={styles.topBtn}
iconSource={() => (
<Octicons name="pencil" size={24} color="#919191" />
)}
onPress={() => {}}
iconSource={() => <PenIcon />}
onPress={() => {
console.log("selview");
}}
/>
<Button
style={styles.topBtn}
iconSource={() => (
<EvilIcons name="trash" size={30} color="#919191" />
)}
onPress={() => {}}
marginL-5
iconSource={() => <BinIcon />}
onPress={() => {
deleteBrainDump(props.item.id);
props.setIsVisible(false);
}}
/>
</View>
</View>

View File

@ -1,402 +1,415 @@
import {AntDesign, Ionicons} from "@expo/vector-icons";
import React, {useCallback, useState} from "react";
import {Button, Checkbox, Text, View} from "react-native-ui-lib";
import {ScrollView, StyleSheet} from "react-native";
import {colorMap} from "@/contexts/SettingsContext";
import {TouchableOpacity} from "react-native-gesture-handler";
import {fetchGoogleCalendarEvents} from "@/calendar-integration/google-calendar-utils";
import {fetchMicrosoftCalendarEvents} from "@/calendar-integration/microsoft-calendar-utils";
import {useCreateEventFromProvider} from "@/hooks/firebase/useCreateEvent";
import {useAuthContext} from "@/contexts/AuthContext";
import {useUpdateUserData} from "@/hooks/firebase/useUpdateUserData";
import {GoogleSignin} from "@react-native-google-signin/google-signin";
import { AntDesign, Ionicons } from "@expo/vector-icons";
import React, { useCallback, useState } from "react";
import { Button, Checkbox, Text, View } from "react-native-ui-lib";
import { ScrollView, StyleSheet } from "react-native";
import { colorMap } from "@/contexts/SettingsContext";
import { TouchableOpacity } from "react-native-gesture-handler";
import { fetchGoogleCalendarEvents } from "@/calendar-integration/google-calendar-utils";
import { fetchMicrosoftCalendarEvents } from "@/calendar-integration/microsoft-calendar-utils";
import { useCreateEventFromProvider } from "@/hooks/firebase/useCreateEvent";
import { useAuthContext } from "@/contexts/AuthContext";
import { useUpdateUserData } from "@/hooks/firebase/useUpdateUserData";
import { GoogleSignin } from "@react-native-google-signin/google-signin";
import * as AuthSession from "expo-auth-session";
import debounce from "debounce";
import AppleIcon from "@/assets/svgs/AppleIcon";
import GoogleIcon from "@/assets/svgs/GoogleIcon";
import OutlookIcon from "@/assets/svgs/OutlookIcon";
GoogleSignin.configure({
webClientId:
"406146460310-hjadmfa1gg4ptaouira5rkhu0djlo5ut.apps.googleusercontent.com",
scopes: ["profile", "email"], // Note: add calendar scope
webClientId:
"406146460310-hjadmfa1gg4ptaouira5rkhu0djlo5ut.apps.googleusercontent.com",
scopes: ["profile", "email"], // Note: add calendar scope
});
const GoogleLogin = async () => {
return await GoogleSignin.signIn();
return await GoogleSignin.signIn();
};
const microsoftConfig = {
clientId: "13c79071-1066-40a9-9f71-b8c4b138b4af", // Replace with your Microsoft client ID
redirectUri: AuthSession.makeRedirectUri({path: "settings"}), // Generate redirect URI automatically for Expo
scopes: [
"openid",
"profile",
"email",
"offline_access",
"Calendars.ReadWrite", // Scope for reading calendar events
],
authorizationEndpoint:
"https://login.microsoftonline.com/common/oauth2/v2.0/authorize",
tokenEndpoint: "https://login.microsoftonline.com/common/oauth2/v2.0/token"
clientId: "13c79071-1066-40a9-9f71-b8c4b138b4af", // Replace with your Microsoft client ID
redirectUri: AuthSession.makeRedirectUri({ path: "settings" }), // Generate redirect URI automatically for Expo
scopes: [
"openid",
"profile",
"email",
"offline_access",
"Calendars.ReadWrite", // Scope for reading calendar events
],
authorizationEndpoint:
"https://login.microsoftonline.com/common/oauth2/v2.0/authorize",
tokenEndpoint: "https://login.microsoftonline.com/common/oauth2/v2.0/token",
};
const CalendarSettingsPage = (props: {
setSelectedPage: (page: number) => void;
setSelectedPage: (page: number) => void;
}) => {
const [startDate, setStartDate] = useState<boolean>(true);
const {profileData} = useAuthContext();
const [startDate, setStartDate] = useState<boolean>(false);
const { profileData } = useAuthContext();
const [selectedColor, setSelectedColor] = useState<string>(profileData?.eventColor ?? colorMap.pink);
const [previousSelectedColor, setPreviousSelectedColor] = useState<string>(profileData?.eventColor ?? colorMap.pink);
const [selectedColor, setSelectedColor] = useState<string>(
profileData?.eventColor ?? colorMap.pink
);
const [previousSelectedColor, setPreviousSelectedColor] = useState<string>(
profileData?.eventColor ?? colorMap.pink
);
const {mutateAsync: createEventFromProvider} = useCreateEventFromProvider();
const {mutateAsync: updateUserData} = useUpdateUserData();
const { mutateAsync: createEventFromProvider } = useCreateEventFromProvider();
const { mutateAsync: updateUserData } = useUpdateUserData();
const fetchAndSaveGoogleEvents = () => {
const timeMin = new Date(new Date().setHours(0, 0, 0, 0));
const timeMax = new Date(
new Date(new Date().setHours(0, 0, 0, 0)).setDate(timeMin.getDate() + 30)
);
const fetchAndSaveGoogleEvents = () => {
const timeMin = new Date(new Date().setHours(0, 0, 0, 0));
const timeMax = new Date(
new Date(new Date().setHours(0, 0, 0, 0)).setDate(timeMin.getDate() + 30)
);
fetchGoogleCalendarEvents(
profileData?.googleToken,
timeMin.toISOString().slice(0, -5) + "Z",
timeMax.toISOString().slice(0, -5) + "Z",
).then((response) => {
response?.forEach((item) => saveData(item));
});
};
fetchGoogleCalendarEvents(
profileData?.googleToken,
timeMin.toISOString().slice(0, -5) + "Z",
timeMax.toISOString().slice(0, -5) + "Z"
).then((response) => {
response?.forEach((item) => saveData(item));
});
};
async function saveData(item: any) {
await createEventFromProvider(item);
async function saveData(item: any) {
await createEventFromProvider(item);
}
const fetchAndSaveMicrosoftEvents = () => {
const startDateTime = new Date(new Date().setHours(0, 0, 0, 0));
const endDateTime = new Date(
new Date(new Date().setHours(0, 0, 0, 0)).setDate(
startDateTime.getDate() + 30
)
);
fetchMicrosoftCalendarEvents(
profileData?.microsoftToken,
startDateTime.toISOString().slice(0, -5) + "Z",
endDateTime.toISOString().slice(0, -5) + "Z"
).then((response) => {
console.log(response);
response?.forEach((item) => saveData(item));
});
};
const handleGoogleLogin = async () => {
try {
const response = await GoogleLogin();
if (response) {
const googleUserData = response.data;
let idToken = googleUserData?.idToken;
if (idToken) {
await updateUserData({ newUserData: { googleToken: idToken } });
}
}
} catch (apiError) {
console.log(apiError || "Something went wrong");
}
};
const fetchAndSaveMicrosoftEvents = () => {
const startDateTime = new Date(new Date().setHours(0, 0, 0, 0));
const endDateTime = new Date(
new Date(new Date().setHours(0, 0, 0, 0)).setDate(
startDateTime.getDate() + 30
)
);
const handleMicrosoftSignIn = async () => {
try {
console.log("Starting Microsoft sign-in...");
const authRequest = new AuthSession.AuthRequest({
clientId: microsoftConfig.clientId,
scopes: microsoftConfig.scopes,
redirectUri: microsoftConfig.redirectUri,
responseType: AuthSession.ResponseType.Code,
usePKCE: true, // Enable PKCE
});
fetchMicrosoftCalendarEvents(
profileData?.microsoftToken,
startDateTime.toISOString().slice(0, -5) + "Z",
endDateTime.toISOString().slice(0, -5) + "Z"
).then((response) => {
console.log(response)
response?.forEach((item) => saveData(item));
console.log("Auth request created:", authRequest);
const authResult = await authRequest.promptAsync({
authorizationEndpoint: microsoftConfig.authorizationEndpoint,
});
console.log("Auth result:", authResult);
if (authResult.type === "success" && authResult.params?.code) {
const code = authResult.params.code;
console.log("Authorization code received:", code);
// Exchange authorization code for tokens
const tokenResponse = await fetch(microsoftConfig.tokenEndpoint, {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body: `client_id=${
microsoftConfig.clientId
}&redirect_uri=${encodeURIComponent(
microsoftConfig.redirectUri
)}&grant_type=authorization_code&code=${code}&code_verifier=${
authRequest.codeVerifier
}&scope=${encodeURIComponent(
"https://graph.microsoft.com/Calendars.ReadWrite offline_access"
)}`,
});
};
const handleGoogleLogin = async () => {
try {
const response = await GoogleLogin();
if (response) {
const googleUserData = response.data;
let idToken = googleUserData?.idToken;
console.log("Token response status:", tokenResponse.status);
if (idToken) {
await updateUserData({newUserData: {googleToken: idToken}});
}
}
} catch (apiError) {
console.log(apiError || "Something went wrong");
if (!tokenResponse.ok) {
console.error("Token exchange failed:", await tokenResponse.text());
return;
}
};
const handleMicrosoftSignIn = async () => {
try {
console.log("Starting Microsoft sign-in...");
const tokenData = await tokenResponse.json();
console.log("Token data received:", tokenData);
const authRequest = new AuthSession.AuthRequest({
clientId: microsoftConfig.clientId,
scopes: microsoftConfig.scopes,
redirectUri: microsoftConfig.redirectUri,
responseType: AuthSession.ResponseType.Code,
usePKCE: true, // Enable PKCE
});
console.log("Auth request created:", authRequest);
const authResult = await authRequest.promptAsync({
authorizationEndpoint: microsoftConfig.authorizationEndpoint,
});
console.log("Auth result:", authResult);
if (authResult.type === "success" && authResult.params?.code) {
const code = authResult.params.code;
console.log("Authorization code received:", code);
// Exchange authorization code for tokens
const tokenResponse = await fetch(microsoftConfig.tokenEndpoint, {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body: `client_id=${microsoftConfig.clientId}&redirect_uri=${encodeURIComponent(
microsoftConfig.redirectUri
)}&grant_type=authorization_code&code=${code}&code_verifier=${
authRequest.codeVerifier
}&scope=${encodeURIComponent("https://graph.microsoft.com/Calendars.ReadWrite offline_access")}`,
});
console.log("Token response status:", tokenResponse.status);
if (!tokenResponse.ok) {
console.error("Token exchange failed:", await tokenResponse.text());
return;
}
const tokenData = await tokenResponse.json();
console.log("Token data received:", tokenData);
if (tokenData?.id_token) {
console.log("ID token received, updating user data...");
await updateUserData({newUserData: {microsoftToken: tokenData.access_token}});
console.log("User data updated successfully.");
}
} else {
console.warn("Authentication was not successful:", authResult);
}
} catch (error) {
console.error("Error during Microsoft sign-in:", error);
if (tokenData?.id_token) {
console.log("ID token received, updating user data...");
await updateUserData({
newUserData: { microsoftToken: tokenData.access_token },
});
console.log("User data updated successfully.");
}
};
} else {
console.warn("Authentication was not successful:", authResult);
}
} catch (error) {
console.error("Error during Microsoft sign-in:", error);
}
};
const debouncedUpdateUserData = useCallback(
debounce(async (color: string) => {
try {
await updateUserData({
newUserData: {
eventColor: color
}
});
} catch (error) {
console.error("Failed to update color:", error);
setSelectedColor(previousSelectedColor)
}
}, 500),
[]
);
const debouncedUpdateUserData = useCallback(
debounce(async (color: string) => {
try {
await updateUserData({
newUserData: {
eventColor: color,
},
});
} catch (error) {
console.error("Failed to update color:", error);
setSelectedColor(previousSelectedColor);
}
}, 500),
[]
);
const handleChangeColor = (color: string) => {
setPreviousSelectedColor(selectedColor)
setSelectedColor(color);
debouncedUpdateUserData(color);
};
const handleChangeColor = (color: string) => {
setPreviousSelectedColor(selectedColor);
setSelectedColor(color);
debouncedUpdateUserData(color);
};
return (
<ScrollView>
<View marginH-30>
<TouchableOpacity onPress={() => props.setSelectedPage(0)}>
<View row marginT-20 marginB-35 centerV>
<Ionicons name="chevron-back" size={22} color="#979797"/>
<Text text70 color="#979797">
Return to main settings
</Text>
</View>
</TouchableOpacity>
<Text text60R>Calendar settings</Text>
<View style={styles.card}>
<Text text70 marginB-14>
Event Color Preference
</Text>
<View row spread>
<TouchableOpacity onPress={() => handleChangeColor(colorMap.pink)}>
<View style={styles.colorBox} backgroundColor={colorMap.pink}>
{selectedColor == colorMap.pink && (
<AntDesign name="check" size={30} color="white"/>
)}
</View>
</TouchableOpacity>
<TouchableOpacity onPress={() => handleChangeColor(colorMap.orange)}>
<View style={styles.colorBox} backgroundColor={colorMap.orange}>
{selectedColor == colorMap.orange && (
<AntDesign name="check" size={30} color="white"/>
)}
</View>
</TouchableOpacity>
<TouchableOpacity onPress={() => handleChangeColor(colorMap.green)}>
<View style={styles.colorBox} backgroundColor={colorMap.green}>
{selectedColor == colorMap.green && (
<AntDesign name="check" size={30} color="white"/>
)}
</View>
</TouchableOpacity>
<TouchableOpacity onPress={() => handleChangeColor(colorMap.teal)}>
<View style={styles.colorBox} backgroundColor={colorMap.teal}>
{selectedColor == colorMap.teal && (
<AntDesign name="check" size={30} color="white"/>
)}
</View>
</TouchableOpacity>
<TouchableOpacity onPress={() => handleChangeColor(colorMap.purple)}>
<View style={styles.colorBox} backgroundColor={colorMap.purple}>
{selectedColor == colorMap.purple && (
<AntDesign name="check" size={30} color="white"/>
)}
</View>
</TouchableOpacity>
</View>
</View>
<View style={styles.card}>
<Text text70>Weekly Start Date</Text>
<View row marginV-5 marginT-20>
<Checkbox
value={startDate}
style={styles.checkbox}
color="#ea156d"
onValueChange={() => setStartDate(true)}
/>
<View row marginL-8>
<Text text70>Sundays</Text>
<Text text70 color="gray">
{" "}
(default)
</Text>
</View>
</View>
<View row marginV-5>
<Checkbox
value={!startDate}
style={styles.checkbox}
color="#ea156d"
onValueChange={() => setStartDate(false)}
/>
<Text text70 marginL-8>
Mondays
</Text>
</View>
</View>
<View style={styles.card}>
<Text text70>Add Calendar</Text>
<View style={{marginTop: 20}}>
<Button
label={"Connect Google"}
iconSource={() => (
<View
backgroundColor="#ededed"
width={40}
height={40}
style={{borderRadius: 50}}
marginR-10
centerV
centerH
>
<Ionicons name="logo-google" size={22} color="#979797"/>
</View>
)}
backgroundColor="white"
color="#464039"
borderRadius={15}
onPress={handleGoogleLogin}
/>
<Button
label={"Connect Microsoft"}
iconSource={() => (
<View
backgroundColor="#ededed"
width={40}
height={40}
style={{borderRadius: 50}}
marginR-10
centerV
centerH
>
<Ionicons name="logo-microsoft" size={22} color="#979797"/>
</View>
)}
backgroundColor="white"
color="#464039"
borderRadius={15}
onPress={handleMicrosoftSignIn}
/>
</View>
</View>
<View style={styles.card}>
<Text text70>Calendars</Text>
<View style={{marginTop: 20}}>
<Button
label={"Sync Outlook"}
iconSource={() => (
<View
backgroundColor="#ededed"
width={40}
height={40}
style={{borderRadius: 50}}
marginR-10
centerV
centerH
>
<Ionicons name="logo-microsoft" size={22} color="#979797"/>
</View>
)}
backgroundColor="white"
color="#464039"
borderRadius={15}
onPress={fetchAndSaveMicrosoftEvents}
/>
{profileData?.googleToken !== undefined && (
<Button
label={"Sync Google"}
iconSource={() => (
<View
backgroundColor="#ededed"
width={40}
height={40}
style={{borderRadius: 50}}
marginR-10
centerV
centerH
>
<Ionicons name="logo-google" size={22} color="#979797"/>
</View>
)}
backgroundColor="white"
color="#464039"
borderRadius={15}
onPress={fetchAndSaveGoogleEvents}
/>
)}
</View>
</View>
return (
<ScrollView>
<View marginH-30>
<TouchableOpacity onPress={() => props.setSelectedPage(0)}>
<View row marginT-20 marginB-35 centerV>
<Ionicons name="chevron-back" size={22} color="#979797" />
<Text text70 color="#979797">
Return to main settings
</Text>
</View>
</TouchableOpacity>
<Text text60R>Calendar settings</Text>
<View style={styles.card}>
<Text text70 marginB-14>
Event Color Preference
</Text>
<View row spread>
<TouchableOpacity onPress={() => handleChangeColor(colorMap.pink)}>
<View style={styles.colorBox} backgroundColor={colorMap.pink}>
{selectedColor == colorMap.pink && (
<AntDesign name="check" size={30} color="white" />
)}
</View>
</TouchableOpacity>
<TouchableOpacity
onPress={() => handleChangeColor(colorMap.orange)}
>
<View style={styles.colorBox} backgroundColor={colorMap.orange}>
{selectedColor == colorMap.orange && (
<AntDesign name="check" size={30} color="white" />
)}
</View>
</TouchableOpacity>
<TouchableOpacity onPress={() => handleChangeColor(colorMap.green)}>
<View style={styles.colorBox} backgroundColor={colorMap.green}>
{selectedColor == colorMap.green && (
<AntDesign name="check" size={30} color="white" />
)}
</View>
</TouchableOpacity>
<TouchableOpacity onPress={() => handleChangeColor(colorMap.teal)}>
<View style={styles.colorBox} backgroundColor={colorMap.teal}>
{selectedColor == colorMap.teal && (
<AntDesign name="check" size={30} color="white" />
)}
</View>
</TouchableOpacity>
<TouchableOpacity
onPress={() => handleChangeColor(colorMap.purple)}
>
<View style={styles.colorBox} backgroundColor={colorMap.purple}>
{selectedColor == colorMap.purple && (
<AntDesign name="check" size={30} color="white" />
)}
</View>
</TouchableOpacity>
</View>
</View>
<View style={styles.card}>
<Text text70>Weekly Start Date</Text>
<View row marginV-5 marginT-20>
<Checkbox
value={startDate}
style={styles.checkbox}
color="#ea156d"
onValueChange={() => setStartDate(true)}
/>
<View row marginL-8>
<Text text70>Sundays</Text>
<Text text70 color="gray">
{" "}
(default)
</Text>
</View>
</ScrollView>
);
</View>
<View row marginV-5>
<Checkbox
value={!startDate}
style={styles.checkbox}
color="#ea156d"
onValueChange={() => setStartDate(false)}
/>
<Text text70 marginL-8>
Mondays
</Text>
</View>
</View>
<Text text60R marginT-30 marginB-25>
Add Calendar
</Text>
<Button
onPress={handleGoogleLogin}
label="Connect Google"
iconSource={() => (
<View marginR-15>
<GoogleIcon />
</View>
)}
style={styles.addCalBtn}
color="black"
text70BL
/>
<Button
label="Connect Apple"
iconSource={() => (
<View marginR-15>
<AppleIcon />
</View>
)}
style={styles.addCalBtn}
color="black"
text70BL
/>
<Button
onPress={handleMicrosoftSignIn}
label="Connect Outlook"
iconSource={() => (
<View marginR-15>
<OutlookIcon />
</View>
)}
style={styles.addCalBtn}
color="black"
text70BL
/>
<Text text60R marginT-30 marginB-20>
Connected Calendars
</Text>
<View style={styles.card}>
<Text text70>Calendars</Text>
<View style={{ marginTop: 20 }}>
<Button
label={"Sync Outlook"}
iconSource={() => (
<View
backgroundColor="#ededed"
width={40}
height={40}
style={{ borderRadius: 50 }}
marginR-10
centerV
centerH
>
<Ionicons name="logo-microsoft" size={22} color="#979797" />
</View>
)}
backgroundColor="white"
color="#464039"
borderRadius={15}
onPress={fetchAndSaveMicrosoftEvents}
/>
{profileData?.googleToken !== undefined && (
<Button
label={"Sync Google"}
iconSource={() => (
<View
backgroundColor="#ededed"
width={40}
height={40}
style={{ borderRadius: 50 }}
marginR-10
centerV
centerH
>
<Ionicons name="logo-google" size={22} color="#979797" />
</View>
)}
backgroundColor="white"
color="#464039"
borderRadius={15}
onPress={fetchAndSaveGoogleEvents}
/>
)}
</View>
</View>
</View>
</ScrollView>
);
};
const styles = StyleSheet.create({
addCalBtn: {
backgroundColor: "#ffffff",
marginBottom: 15,
justifyContent: "flex-start",
paddingLeft: 25,
},
backBtn: {
backgroundColor: "red",
marginLeft: -2,
justifyContent: "flex-start",
},
card: {
backgroundColor: "white",
width: "100%",
padding: 20,
paddingBottom: 30,
marginTop: 20,
borderRadius: 20,
},
colorBox: {
aspectRatio: 1,
justifyContent: "center",
alignItems: "center",
width: 50,
borderRadius: 12,
},
checkbox: {
borderRadius: 50,
},
addCalBtn: {
backgroundColor: "#ffffff",
marginBottom: 15,
justifyContent: "flex-start",
paddingLeft: 25,
},
backBtn: {
backgroundColor: "red",
marginLeft: -2,
justifyContent: "flex-start",
},
card: {
backgroundColor: "white",
width: "100%",
padding: 20,
paddingBottom: 30,
marginTop: 20,
borderRadius: 20,
},
colorBox: {
aspectRatio: 1,
justifyContent: "center",
alignItems: "center",
width: 50,
borderRadius: 12,
},
checkbox: {
borderRadius: 50,
},
});
export default CalendarSettingsPage;

View File

@ -55,7 +55,7 @@ const UserSettings = (props: { setSelectedPage: (page: number) => void }) => {
{!selectedView && (
<View>
<Text>Nigga</Text>
<Text>selview</Text>
</View>
)}
</View>

View File

@ -13,6 +13,7 @@ interface IBrainDumpContext {
isAddingBrainDump: boolean;
setIsAddingBrainDump: (value: boolean) => void;
addBrainDump: (BrainDump: IBrainDump) => void;
deleteBrainDump: (id: number) => void;
}
const BrainDumpContext = createContext<IBrainDumpContext | undefined>(
@ -82,6 +83,12 @@ export const BrainDumpProvider: React.FC<{ children: React.ReactNode }> = ({
);
};
const deleteBrainDump = (id: number) => {
setBrainDumps((prevBrainDumps) =>
prevBrainDumps.filter((BrainDump) => BrainDump.id !== id)
);
};
return (
<BrainDumpContext.Provider
value={{
@ -90,6 +97,7 @@ export const BrainDumpProvider: React.FC<{ children: React.ReactNode }> = ({
isAddingBrainDump,
setIsAddingBrainDump,
addBrainDump,
deleteBrainDump
}}
>
{children}