mirror of
https://github.com/urosran/cally.git
synced 2025-07-11 15:47:21 +00:00
474 lines
24 KiB
TypeScript
474 lines
24 KiB
TypeScript
import {Ionicons} from "@expo/vector-icons";
|
|
import React, {useCallback, useState} from "react";
|
|
import {Button, Checkbox, Text, View} from "react-native-ui-lib";
|
|
import {ActivityIndicator, ScrollView, StyleSheet} from "react-native";
|
|
import {TouchableOpacity} from "react-native-gesture-handler";
|
|
import {useAuthContext} from "@/contexts/AuthContext";
|
|
import {useUpdateUserData} from "@/hooks/firebase/useUpdateUserData";
|
|
import debounce from "debounce";
|
|
import AppleIcon from "@/assets/svgs/AppleIcon";
|
|
import GoogleIcon from "@/assets/svgs/GoogleIcon";
|
|
import OutlookIcon from "@/assets/svgs/OutlookIcon";
|
|
import ExpoLocalization from "expo-localization/src/ExpoLocalization";
|
|
import {useSetAtom} from "jotai";
|
|
import {settingsPageIndex} from "../calendar/atoms";
|
|
import CalendarSettingsDialog from "./calendar_components/CalendarSettingsDialog";
|
|
import {useClearTokens} from "@/hooks/firebase/useClearTokens";
|
|
import {useCalSync} from "@/hooks/useCalSync";
|
|
import Feather from "@expo/vector-icons/Feather";
|
|
|
|
|
|
const CalendarSettingsPage = () => {
|
|
const {profileData} = useAuthContext();
|
|
const setPageIndex = useSetAtom(settingsPageIndex);
|
|
|
|
const [firstDayOfWeek, setFirstDayOfWeek] = useState<string>(
|
|
profileData?.firstDayOfWeek ??
|
|
ExpoLocalization.getCalendars()[0].firstWeekday === 1
|
|
? "Mondays"
|
|
: "Sundays"
|
|
);
|
|
const [isModalVisible, setModalVisible] = useState<boolean>(false);
|
|
const [selectedService, setSelectedService] = useState<
|
|
"google" | "outlook" | "apple"
|
|
>("google");
|
|
const [selectedEmail, setSelectedEmail] = useState<string>("");
|
|
|
|
const showConfirmationDialog = (
|
|
serviceName: "google" | "outlook" | "apple",
|
|
email: string
|
|
) => {
|
|
setSelectedService(serviceName);
|
|
setSelectedEmail(email);
|
|
setModalVisible(true);
|
|
};
|
|
|
|
const handleConfirm = async () => {
|
|
await clearToken({email: selectedEmail, provider: selectedService});
|
|
setModalVisible(false);
|
|
};
|
|
|
|
const handleCancel = () => {
|
|
setModalVisible(false);
|
|
};
|
|
|
|
const {mutateAsync: updateUserData} = useUpdateUserData();
|
|
const {mutateAsync: clearToken} = useClearTokens();
|
|
|
|
const {
|
|
isSyncingGoogle,
|
|
isSyncingOutlook,
|
|
isConnectedToGoogle,
|
|
isConnectedToMicrosoft,
|
|
isConnectedToApple,
|
|
handleAppleSignIn,
|
|
isSyncingApple,
|
|
handleMicrosoftSignIn,
|
|
fetchAndSaveOutlookEvents,
|
|
fetchAndSaveGoogleEvents,
|
|
handleStartGoogleSignIn,
|
|
fetchAndSaveAppleEvents
|
|
} = useCalSync()
|
|
|
|
const debouncedUpdateFirstDayOfWeek = useCallback(
|
|
debounce(async (firstDayOfWeek: string) => {
|
|
try {
|
|
await updateUserData({
|
|
newUserData: {
|
|
firstDayOfWeek,
|
|
},
|
|
});
|
|
} catch (error) {
|
|
console.error("Failed to update first day of week:", error);
|
|
}
|
|
}, 500),
|
|
[]
|
|
);
|
|
|
|
const handleChangeFirstDayOfWeek = (firstDayOfWeek: string) => {
|
|
setFirstDayOfWeek(firstDayOfWeek);
|
|
debouncedUpdateFirstDayOfWeek(firstDayOfWeek);
|
|
};
|
|
|
|
return (
|
|
<ScrollView>
|
|
<TouchableOpacity onPress={() => setPageIndex(0)}>
|
|
<View row marginT-20 marginB-20 marginL-20 centerV>
|
|
<Ionicons
|
|
name="chevron-back"
|
|
size={14}
|
|
color="#979797"
|
|
style={{paddingBottom: 3}}
|
|
/>
|
|
<Text
|
|
style={{fontFamily: "Poppins_400Regular", fontSize: 14.71}}
|
|
color="#979797"
|
|
>
|
|
Return to main settings
|
|
</Text>
|
|
</View>
|
|
</TouchableOpacity>
|
|
<View marginH-30 marginB-30>
|
|
<Text style={styles.subTitle}>Calendar settings</Text>
|
|
<View style={styles.card}>
|
|
<Text style={styles.cardTitle}>Weekly Start Date</Text>
|
|
<View row marginV-5 marginT-20>
|
|
<Checkbox
|
|
value={firstDayOfWeek === "Sundays"}
|
|
style={[styles.checkbox, {borderRadius: 100}]}
|
|
borderRadius={100}
|
|
color="#ea156d"
|
|
onValueChange={() => handleChangeFirstDayOfWeek("Sundays")}
|
|
/>
|
|
<View row marginL-8>
|
|
<Text text70>Sundays</Text>
|
|
<Text text70 color="gray">
|
|
{" "}
|
|
(default)
|
|
</Text>
|
|
</View>
|
|
</View>
|
|
<View row marginV-5>
|
|
<Checkbox
|
|
value={firstDayOfWeek === "Mondays"}
|
|
style={[styles.checkbox, {borderRadius: 50}]}
|
|
color="#ea156d"
|
|
borderRadius={50}
|
|
onValueChange={() => handleChangeFirstDayOfWeek("Mondays")}
|
|
/>
|
|
<Text text70 marginL-8>
|
|
Mondays
|
|
</Text>
|
|
</View>
|
|
</View>
|
|
<Text style={styles.subTitle} marginT-30 marginB-25>
|
|
Add Calendars
|
|
</Text>
|
|
|
|
<Button
|
|
onPress={() => handleStartGoogleSignIn()}
|
|
label={profileData?.googleAccounts ? "Connect another Google account" : "Connect Google account"}
|
|
labelStyle={styles.addCalLbl}
|
|
labelProps={{
|
|
numberOfLines: 2,
|
|
}}
|
|
iconSource={() => (
|
|
<View marginR-15>
|
|
<GoogleIcon/>
|
|
</View>
|
|
)}
|
|
style={styles.addCalBtn}
|
|
color="black"
|
|
text70BL
|
|
/>
|
|
|
|
{!profileData?.appleAccounts && (
|
|
<Button
|
|
onPress={() => handleAppleSignIn()}
|
|
label={"Connect Apple"}
|
|
labelStyle={styles.addCalLbl}
|
|
labelProps={{
|
|
numberOfLines: 2,
|
|
}}
|
|
iconSource={() => (
|
|
<View marginR-15>
|
|
<AppleIcon/>
|
|
</View>
|
|
)}
|
|
style={styles.addCalBtn}
|
|
color="black"
|
|
text70BL
|
|
/>
|
|
)}
|
|
|
|
<Button
|
|
onPress={() => handleMicrosoftSignIn()}
|
|
label={profileData?.microsoftAccounts ? "Connect another Outlook account" : "Connect Outlook"}
|
|
labelStyle={styles.addCalLbl}
|
|
labelProps={{
|
|
numberOfLines: 2,
|
|
}}
|
|
iconSource={() => (
|
|
<View marginR-15>
|
|
<OutlookIcon/>
|
|
</View>
|
|
)}
|
|
style={styles.addCalBtn}
|
|
color="black"
|
|
text70BL
|
|
/>
|
|
|
|
{(isConnectedToGoogle ||
|
|
isConnectedToMicrosoft ||
|
|
isConnectedToApple) && (
|
|
<>
|
|
<Text style={styles.subTitle} marginT-30 marginB-20>
|
|
Connected Calendars
|
|
</Text>
|
|
|
|
<View style={[styles.noPaddingCard, {marginBottom: 100}]}>
|
|
<View style={{marginTop: 20}}>
|
|
{profileData?.googleAccounts &&
|
|
Object.keys(profileData?.googleAccounts)?.map(
|
|
(googleEmail) => {
|
|
const googleToken =
|
|
profileData?.googleAccounts?.[googleEmail]?.accessToken;
|
|
return (
|
|
googleToken && (
|
|
<View row paddingR-5 center>
|
|
<View
|
|
style={{
|
|
backgroundColor: "#ffffff",
|
|
marginBottom: 15,
|
|
paddingLeft: 15,
|
|
}}
|
|
color="black"
|
|
text70BL
|
|
row
|
|
centerV
|
|
width="100%"
|
|
spread
|
|
>
|
|
{isSyncingGoogle ? (
|
|
<View marginR-5>
|
|
<ActivityIndicator/>
|
|
</View>
|
|
) : (
|
|
<View marginR-5>
|
|
<Button
|
|
style={{backgroundColor: "#ffffff"}}
|
|
color="black"
|
|
onPress={() =>
|
|
fetchAndSaveGoogleEvents({
|
|
token: googleToken,
|
|
email: googleEmail,
|
|
})
|
|
}
|
|
iconSource={() => <Ionicons
|
|
name={"refresh"}
|
|
size={20}
|
|
color={"#000000"}
|
|
/>}
|
|
/>
|
|
</View>
|
|
)}
|
|
<View marginR-5>
|
|
<GoogleIcon/>
|
|
</View>
|
|
<Text style={styles.addCalLbl}>
|
|
{googleEmail}
|
|
</Text>
|
|
<Button
|
|
style={{backgroundColor: "#ffffff", marginRight: 5}}
|
|
color="black"
|
|
onPress={
|
|
() => showConfirmationDialog("google", googleEmail)
|
|
}
|
|
iconSource={() => <Feather name="x" size={24} />}
|
|
/>
|
|
</View>
|
|
</View>
|
|
)
|
|
);
|
|
}
|
|
)}
|
|
|
|
{profileData?.appleAccounts &&
|
|
Object.keys(profileData?.appleAccounts)?.map((appleEmail) => {
|
|
console.log(profileData?.appleAccounts)
|
|
|
|
const appleToken = profileData?.appleAccounts?.[appleEmail];
|
|
return (
|
|
appleToken && (
|
|
<View row paddingR-5 center>
|
|
<View
|
|
style={{
|
|
backgroundColor: "#ffffff",
|
|
marginBottom: 15,
|
|
paddingLeft: 15,
|
|
}}
|
|
color="black"
|
|
text70BL
|
|
row
|
|
centerV
|
|
width="100%"
|
|
spread
|
|
>
|
|
<View marginR-5>
|
|
<AppleIcon/>
|
|
</View>
|
|
<Text style={styles.addCalLbl}>
|
|
{appleEmail}
|
|
</Text>
|
|
{isSyncingApple ? (
|
|
<View marginR-5>
|
|
<ActivityIndicator/>
|
|
</View>
|
|
) : (
|
|
<View marginR-5>
|
|
<Button
|
|
style={{backgroundColor: "#ffffff"}}
|
|
color="black"
|
|
onPress={() =>
|
|
fetchAndSaveAppleEvents({
|
|
email: appleEmail,
|
|
token: appleToken,
|
|
})
|
|
}
|
|
iconSource={() => <Ionicons
|
|
name={"refresh"}
|
|
size={20}
|
|
color={"#000000"}
|
|
/>}
|
|
/>
|
|
</View>
|
|
)}
|
|
<Button
|
|
style={{backgroundColor: "#ffffff", marginRight: 5}}
|
|
color="black"
|
|
onPress={() => showConfirmationDialog("apple", appleEmail)}
|
|
iconSource={() => <Feather name="x" size={24} />}
|
|
/>
|
|
</View>
|
|
</View>
|
|
)
|
|
);
|
|
})}
|
|
|
|
{profileData?.microsoftAccounts &&
|
|
Object.keys(profileData?.microsoftAccounts)?.map(
|
|
(microsoftEmail) => {
|
|
const microsoftToken =
|
|
profileData?.microsoftAccounts?.[microsoftEmail];
|
|
return (
|
|
microsoftToken && (
|
|
<View row paddingR-5 center>
|
|
<View
|
|
style={{
|
|
backgroundColor: "#ffffff",
|
|
marginBottom: 15,
|
|
paddingLeft: 15,
|
|
}}
|
|
color="black"
|
|
text70BL
|
|
row
|
|
centerV
|
|
width="100%"
|
|
spread
|
|
>
|
|
{isSyncingOutlook ? (
|
|
<View marginR-5>
|
|
<ActivityIndicator/>
|
|
</View>
|
|
) : (
|
|
<View marginR-5>
|
|
<Button
|
|
style={{backgroundColor: "#ffffff"}}
|
|
color="black"
|
|
onPress={() =>
|
|
fetchAndSaveOutlookEvents({
|
|
token: microsoftToken,
|
|
email: microsoftEmail,
|
|
})
|
|
}
|
|
iconSource={() => <Ionicons
|
|
name={"refresh"}
|
|
size={20}
|
|
color={"#000000"}
|
|
/>}
|
|
/>
|
|
</View>
|
|
)}
|
|
<View marginR-5>
|
|
<OutlookIcon/>
|
|
</View>
|
|
<Text style={styles.addCalLbl}>
|
|
{microsoftEmail}
|
|
</Text>
|
|
<Button
|
|
style={{backgroundColor: "#ffffff", marginRight: 5}}
|
|
color="black"
|
|
onPress={
|
|
() => showConfirmationDialog("outlook", microsoftEmail)
|
|
}
|
|
iconSource={() => <Feather name="x" size={24} />}
|
|
/>
|
|
</View>
|
|
</View>
|
|
)
|
|
);
|
|
}
|
|
)}
|
|
</View>
|
|
</View>
|
|
</>
|
|
)}
|
|
</View>
|
|
<CalendarSettingsDialog
|
|
visible={isModalVisible}
|
|
serviceName={selectedService}
|
|
email={selectedEmail}
|
|
onDismiss={handleCancel}
|
|
onConfirm={handleConfirm}
|
|
/>
|
|
</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: 12,
|
|
},
|
|
noPaddingCard: {
|
|
backgroundColor: "white",
|
|
width: "100%",
|
|
marginTop: 20,
|
|
borderRadius: 12,
|
|
},
|
|
colorBox: {
|
|
aspectRatio: 1,
|
|
justifyContent: "center",
|
|
alignItems: "center",
|
|
width: 51,
|
|
borderRadius: 12,
|
|
},
|
|
checkbox: {
|
|
borderRadius: 100,
|
|
},
|
|
addCalLbl: {
|
|
fontSize: 16,
|
|
fontFamily: "PlusJakartaSan_500Medium",
|
|
flexWrap: "wrap",
|
|
width: "70%",
|
|
textAlign: "left",
|
|
lineHeight: 20,
|
|
overflow: "hidden",
|
|
},
|
|
subTitle: {
|
|
fontFamily: "Manrope_600SemiBold",
|
|
fontSize: 18,
|
|
},
|
|
cardTitle: {
|
|
fontFamily: "Manrope_500Medium",
|
|
fontSize: 15,
|
|
},
|
|
});
|
|
|
|
export default CalendarSettingsPage;
|