mirror of
https://github.com/urosran/cally.git
synced 2026-10-23 09:37:54 +00:00
Apple calendar sync, timezone, first day of week additions
This commit is contained in:
@@ -10,6 +10,7 @@ import {
|
||||
selectedDateAtom,
|
||||
selectedNewEventDateAtom
|
||||
} from "@/components/pages/calendar/atoms";
|
||||
import {useAuthContext} from "@/contexts/AuthContext";
|
||||
|
||||
interface EventCalendarProps {
|
||||
calendarHeight: number;
|
||||
@@ -17,6 +18,7 @@ interface EventCalendarProps {
|
||||
|
||||
export const EventCalendar: React.FC<EventCalendarProps> = memo(({calendarHeight}) => {
|
||||
const {data: events} = useGetEvents();
|
||||
const {profileData} = useAuthContext()
|
||||
const [selectedDate, setSelectedDate] = useAtom(selectedDateAtom)
|
||||
const mode = useAtomValue(modeAtom)
|
||||
const setEditVisible = useSetAtom(editVisibleAtom)
|
||||
@@ -35,6 +37,7 @@ export const EventCalendar: React.FC<EventCalendarProps> = memo(({calendarHeight
|
||||
setEditVisible(true);
|
||||
setEventForEdit(event);
|
||||
}}
|
||||
weekStartsOn={profileData?.firstDayOfWeek === "Mondays" ? 1 : 0}
|
||||
height={calendarHeight}
|
||||
activeDate={selectedDate}
|
||||
date={selectedDate}
|
||||
|
||||
@@ -20,7 +20,7 @@ export const InnerCalendar = () => {
|
||||
return (
|
||||
<>
|
||||
<View
|
||||
style={{flex: 1, backgroundColor: "#fff", borderRadius: 30}}
|
||||
style={{flex: 1, backgroundColor: "#fff", borderRadius: 30, marginBottom: 60}}
|
||||
ref={calendarContainerRef}
|
||||
onLayout={onLayout}
|
||||
>
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
import {AntDesign, Ionicons} from "@expo/vector-icons";
|
||||
import React, {useCallback, useEffect, useState} from "react";
|
||||
import {Button, Checkbox, Text, View} from "react-native-ui-lib";
|
||||
import {ScrollView, StyleSheet} from "react-native";
|
||||
import {ActivityIndicator, 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 {useCreateEventsFromProvider} from "@/hooks/firebase/useCreateEvent";
|
||||
import {useAuthContext} from "@/contexts/AuthContext";
|
||||
import {useUpdateUserData} from "@/hooks/firebase/useUpdateUserData";
|
||||
import debounce from "debounce";
|
||||
@@ -17,6 +14,11 @@ import * as AuthSession from "expo-auth-session";
|
||||
import * as Google from "expo-auth-session/providers/google";
|
||||
import * as WebBrowser from "expo-web-browser";
|
||||
import {UserProfile} from "@firebase/auth";
|
||||
import {useFetchAndSaveGoogleEvents} from "@/hooks/useFetchAndSaveGoogleEvents";
|
||||
import {useFetchAndSaveOutlookEvents} from "@/hooks/useFetchAndSaveOutlookEvents";
|
||||
import {useFetchAndSaveAppleEvents} from "@/hooks/useFetchAndSaveAppleEvents";
|
||||
import * as AppleAuthentication from 'expo-apple-authentication';
|
||||
import ExpoLocalization from "expo-localization/src/ExpoLocalization";
|
||||
|
||||
const googleConfig = {
|
||||
androidClientId:
|
||||
@@ -50,8 +52,8 @@ const microsoftConfig = {
|
||||
const CalendarSettingsPage = (props: {
|
||||
setSelectedPage: (page: number) => void;
|
||||
}) => {
|
||||
const [startDate, setStartDate] = useState<boolean>(false);
|
||||
const {profileData} = useAuthContext();
|
||||
const [firstDayOfWeek, setFirstDayOfWeek] = useState<string>(profileData?.firstDayOfWeek ?? ExpoLocalization.getCalendars()[0].firstWeekday === 1 ? "Mondays" : "Sundays");
|
||||
|
||||
const [selectedColor, setSelectedColor] = useState<string>(
|
||||
profileData?.eventColor ?? colorMap.pink
|
||||
@@ -60,8 +62,11 @@ const CalendarSettingsPage = (props: {
|
||||
profileData?.eventColor ?? colorMap.pink
|
||||
);
|
||||
|
||||
const {mutateAsync: createEventsFromProvider} = useCreateEventsFromProvider();
|
||||
const {mutateAsync: updateUserData} = useUpdateUserData();
|
||||
const {mutateAsync: fetchAndSaveGoogleEvents, isLoading: isSyncingGoogle} = useFetchAndSaveGoogleEvents();
|
||||
const {mutateAsync: fetchAndSaveOutlookEvents, isLoading: isSyncingOutlook} = useFetchAndSaveOutlookEvents();
|
||||
const {mutateAsync: fetchAndSaveAppleEvents, isLoading: isSyncingApple} = useFetchAndSaveAppleEvents();
|
||||
|
||||
|
||||
WebBrowser.maybeCompleteAuthSession();
|
||||
const [_, response, promptAsync] = Google.useAuthRequest(googleConfig);
|
||||
@@ -70,49 +75,6 @@ const CalendarSettingsPage = (props: {
|
||||
signInWithGoogle();
|
||||
}, [response]);
|
||||
|
||||
const fetchAndSaveGoogleEvents = async (token?: string, email?: string) => {
|
||||
console.log("Fetching Google Calendar events...");
|
||||
const timeMin = new Date(new Date().setFullYear(new Date().getFullYear() - 1));
|
||||
const timeMax = new Date(new Date().setFullYear(new Date().getFullYear() + 5));
|
||||
|
||||
console.log("Token: ", token ?? profileData?.googleToken)
|
||||
fetchGoogleCalendarEvents(
|
||||
token ?? profileData?.googleToken,
|
||||
email ?? profileData?.googleMail,
|
||||
timeMin.toISOString().slice(0, -5) + "Z",
|
||||
timeMax.toISOString().slice(0, -5) + "Z"
|
||||
).then(async (response) => {
|
||||
console.log("Google Calendar events fetched:", response);
|
||||
const items = response?.map((item) => {
|
||||
if (item.allDay) {
|
||||
item.startDate = new Date(new Date(item.startDate).setHours(0, 0, 0, 0))
|
||||
item.endDate = item.startDate
|
||||
}
|
||||
return item;
|
||||
}) || [];
|
||||
await createEventsFromProvider(items);
|
||||
}).catch((error) => {
|
||||
console.error("Error fetching Google Calendar events:", error);
|
||||
});
|
||||
};
|
||||
|
||||
const fetchAndSaveMicrosoftEvents = async (token?: string, email?: string) => {
|
||||
const timeMin = new Date(new Date().setFullYear(new Date().getFullYear() - 1));
|
||||
const timeMax = new Date(new Date().setFullYear(new Date().getFullYear() + 3));
|
||||
|
||||
console.log("Token: ", token ?? profileData?.microsoftToken)
|
||||
fetchMicrosoftCalendarEvents(
|
||||
token ?? profileData?.microsoftToken,
|
||||
email ?? profileData?.outlookMail,
|
||||
timeMin.toISOString().slice(0, -5) + "Z",
|
||||
timeMax.toISOString().slice(0, -5) + "Z"
|
||||
).then(async (response) => {
|
||||
console.log(response);
|
||||
const items = response ?? [];
|
||||
await createEventsFromProvider(items);
|
||||
});
|
||||
};
|
||||
|
||||
const signInWithGoogle = async () => {
|
||||
try {
|
||||
if (response?.type === "success") {
|
||||
@@ -214,7 +176,7 @@ const CalendarSettingsPage = (props: {
|
||||
newUserData: {microsoftToken: tokenData.access_token, outlookMail: outlookMail},
|
||||
});
|
||||
|
||||
await fetchAndSaveMicrosoftEvents(tokenData.access_token, outlookMail)
|
||||
await fetchAndSaveOutlookEvents(tokenData.access_token, outlookMail)
|
||||
console.log("User data updated successfully.");
|
||||
}
|
||||
}
|
||||
@@ -226,6 +188,39 @@ const CalendarSettingsPage = (props: {
|
||||
}
|
||||
};
|
||||
|
||||
const handleAppleSignIn = async () => {
|
||||
try {
|
||||
console.log("Starting Apple Sign-in...");
|
||||
|
||||
const credential = await AppleAuthentication.signInAsync({
|
||||
requestedScopes: [
|
||||
AppleAuthentication.AppleAuthenticationScope.EMAIL,
|
||||
AppleAuthentication.AppleAuthenticationScope.FULL_NAME,
|
||||
],
|
||||
});
|
||||
|
||||
console.log("Apple sign-in result:", credential);
|
||||
|
||||
const appleToken = credential.identityToken;
|
||||
const appleMail = credential.email;
|
||||
|
||||
if (appleToken) {
|
||||
console.log("Apple ID token received. Fetch user info if needed...");
|
||||
|
||||
// Example: Store user token and email
|
||||
await updateUserData({
|
||||
newUserData: {appleToken, appleMail},
|
||||
});
|
||||
|
||||
console.log("User data updated with Apple ID token.");
|
||||
} else {
|
||||
console.warn("Apple authentication was not successful or email was hidden.");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error during Apple Sign-in:", error);
|
||||
}
|
||||
};
|
||||
|
||||
const debouncedUpdateUserData = useCallback(
|
||||
debounce(async (color: string) => {
|
||||
try {
|
||||
@@ -242,6 +237,26 @@ const CalendarSettingsPage = (props: {
|
||||
[]
|
||||
);
|
||||
|
||||
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 = () => {
|
||||
setFirstDayOfWeek(firstDayOfWeek === "Sundays" ? "Mondays" : "Sundays");
|
||||
debouncedUpdateFirstDayOfWeek(firstDayOfWeek === "Sundays" ? "Mondays" : "Sundays");
|
||||
}
|
||||
|
||||
const handleChangeColor = (color: string) => {
|
||||
setPreviousSelectedColor(selectedColor);
|
||||
setSelectedColor(color);
|
||||
@@ -333,10 +348,10 @@ const CalendarSettingsPage = (props: {
|
||||
<Text style={styles.cardTitle}>Weekly Start Date</Text>
|
||||
<View row marginV-5 marginT-20>
|
||||
<Checkbox
|
||||
value={startDate}
|
||||
value={firstDayOfWeek === "Sundays"}
|
||||
style={styles.checkbox}
|
||||
color="#ea156d"
|
||||
onValueChange={() => setStartDate(true)}
|
||||
onValueChange={() => handleChangeFirstDayOfWeek("Sundays")}
|
||||
/>
|
||||
<View row marginL-8>
|
||||
<Text text70>Sundays</Text>
|
||||
@@ -348,10 +363,10 @@ const CalendarSettingsPage = (props: {
|
||||
</View>
|
||||
<View row marginV-5>
|
||||
<Checkbox
|
||||
value={!startDate}
|
||||
value={firstDayOfWeek === "Mondays"}
|
||||
style={styles.checkbox}
|
||||
color="#ea156d"
|
||||
onValueChange={() => setStartDate(false)}
|
||||
onValueChange={() => handleChangeFirstDayOfWeek("Mondays")}
|
||||
/>
|
||||
<Text text70 marginL-8>
|
||||
Mondays
|
||||
@@ -379,7 +394,8 @@ const CalendarSettingsPage = (props: {
|
||||
text70BL
|
||||
/>
|
||||
<Button
|
||||
label="Connect Apple"
|
||||
onPress={() => !profileData?.appleToken ? handleAppleSignIn() : clearToken("google")}
|
||||
label={profileData?.appleToken ? `Disconnect ${profileData.appleMail}` : "Connect Apple"}
|
||||
labelStyle={styles.addCalLbl}
|
||||
labelProps={{
|
||||
numberOfLines: 2
|
||||
@@ -416,40 +432,93 @@ const CalendarSettingsPage = (props: {
|
||||
Connected Calendars
|
||||
</Text>
|
||||
|
||||
<View style={styles.card}>
|
||||
<View style={styles.noPaddingCard}>
|
||||
<View style={{marginTop: 20}}>
|
||||
{!!profileData?.googleMail && (
|
||||
<Button
|
||||
onPress={() => fetchAndSaveGoogleEvents()}
|
||||
label={`Sync ${profileData?.googleMail}`}
|
||||
labelStyle={styles.addCalLbl}
|
||||
labelProps={{numberOfLines: 2}}
|
||||
iconSource={() => (
|
||||
<View marginR-15>
|
||||
<GoogleIcon/>
|
||||
</View>
|
||||
)}
|
||||
style={styles.addCalBtn}
|
||||
color="black"
|
||||
text70BL
|
||||
/>
|
||||
<TouchableOpacity
|
||||
onPress={() => fetchAndSaveGoogleEvents(undefined, undefined)}
|
||||
>
|
||||
<View row paddingR-20 center>
|
||||
<Button
|
||||
disabled={isSyncingGoogle}
|
||||
onPress={() => fetchAndSaveGoogleEvents(undefined, undefined)}
|
||||
label={`Sync ${profileData?.googleMail}`}
|
||||
labelStyle={styles.addCalLbl}
|
||||
labelProps={{numberOfLines: 3}}
|
||||
iconSource={() => (
|
||||
<View marginR-15>
|
||||
<GoogleIcon/>
|
||||
</View>
|
||||
)}
|
||||
style={styles.addCalBtn}
|
||||
color="black"
|
||||
text70BL
|
||||
/>
|
||||
|
||||
{isSyncingGoogle ? (
|
||||
<ActivityIndicator/>
|
||||
) : (
|
||||
<Ionicons name={"refresh"} size={20} color={"#000000"}/>
|
||||
)}
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
|
||||
|
||||
{!!profileData?.appleMail && (
|
||||
<TouchableOpacity>
|
||||
<View row paddingR-20 center>
|
||||
<Button
|
||||
disabled={isSyncingApple}
|
||||
onPress={() => fetchAndSaveAppleEvents(undefined, undefined)}
|
||||
label={`Sync ${profileData?.appleMail}`}
|
||||
labelStyle={styles.addCalLbl}
|
||||
labelProps={{numberOfLines: 3}}
|
||||
iconSource={() => (
|
||||
<View marginR-15>
|
||||
<AppleIcon/>
|
||||
</View>
|
||||
)}
|
||||
style={styles.addCalBtn}
|
||||
color="black"
|
||||
text70BL
|
||||
/>
|
||||
{isSyncingApple ? (
|
||||
<ActivityIndicator/>
|
||||
) : (
|
||||
<Ionicons name={"refresh"} size={20} color={"#000000"}/>
|
||||
)}
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
|
||||
{!!profileData?.outlookMail && (
|
||||
<Button
|
||||
onPress={() => fetchAndSaveMicrosoftEvents()}
|
||||
label={`Sync ${profileData?.outlookMail}`}
|
||||
labelStyle={styles.addCalLbl}
|
||||
labelProps={{numberOfLines: 2}}
|
||||
iconSource={() => (
|
||||
<View marginR-15>
|
||||
<OutlookIcon/>
|
||||
</View>
|
||||
)}
|
||||
style={styles.addCalBtn}
|
||||
color="black"
|
||||
text70BL
|
||||
/>
|
||||
<TouchableOpacity
|
||||
onPress={() => fetchAndSaveOutlookEvents(undefined, undefined)}
|
||||
>
|
||||
<View row paddingR-20 center>
|
||||
<Button
|
||||
disabled={isSyncingOutlook}
|
||||
onPress={() => fetchAndSaveOutlookEvents(undefined, undefined)}
|
||||
label={`Sync ${profileData?.outlookMail}`}
|
||||
labelStyle={styles.addCalLbl}
|
||||
labelProps={{numberOfLines: 3}}
|
||||
iconSource={() => (
|
||||
<View marginR-15>
|
||||
<OutlookIcon/>
|
||||
</View>
|
||||
)}
|
||||
style={styles.addCalBtn}
|
||||
color="black"
|
||||
text70BL
|
||||
/>
|
||||
{isSyncingOutlook ? (
|
||||
<ActivityIndicator/>
|
||||
) : (
|
||||
<Ionicons name={"refresh"} size={20} color={"#000000"}/>
|
||||
)}
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
@@ -480,6 +549,12 @@ const styles = StyleSheet.create({
|
||||
marginTop: 20,
|
||||
borderRadius: 12,
|
||||
},
|
||||
noPaddingCard: {
|
||||
backgroundColor: "white",
|
||||
width: "100%",
|
||||
marginTop: 20,
|
||||
borderRadius: 12,
|
||||
},
|
||||
colorBox: {
|
||||
aspectRatio: 1,
|
||||
justifyContent: "center",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,122 +1,207 @@
|
||||
import { Text, TextField, View } from "react-native-ui-lib";
|
||||
import React, { useState } from "react";
|
||||
import { ImageBackground, StyleSheet } from "react-native";
|
||||
import { ScrollView } from "react-native-gesture-handler";
|
||||
import { useAuthContext } from "@/contexts/AuthContext";
|
||||
import { useUpdateUserData } from "@/hooks/firebase/useUpdateUserData";
|
||||
import {Colors, Picker, Text, TextField, View} from "react-native-ui-lib";
|
||||
import React, {useEffect, useRef, useState} from "react";
|
||||
import {ImageBackground, StyleSheet} from "react-native";
|
||||
import {ScrollView} from "react-native-gesture-handler";
|
||||
import {useAuthContext} from "@/contexts/AuthContext";
|
||||
import {useUpdateUserData} from "@/hooks/firebase/useUpdateUserData";
|
||||
import Ionicons from "@expo/vector-icons/Ionicons";
|
||||
import * as tz from 'tzdata';
|
||||
import * as Localization from 'expo-localization';
|
||||
import debounce from "debounce";
|
||||
|
||||
const MyProfile = () => {
|
||||
const { user, profileData } = useAuthContext();
|
||||
const {user, profileData} = useAuthContext();
|
||||
|
||||
const [lastName, setLastName] = useState<string>(profileData?.lastName || "");
|
||||
const [firstName, setFirstName] = useState<string>(
|
||||
profileData?.firstName || ""
|
||||
);
|
||||
const [timeZone, setTimeZone] = useState<string>(profileData?.timeZone! ?? Localization.getCalendars()[0].timeZone);
|
||||
const [lastName, setLastName] = useState<string>(profileData?.lastName || "");
|
||||
const [firstName, setFirstName] = useState<string>(
|
||||
profileData?.firstName || ""
|
||||
);
|
||||
|
||||
const { mutateAsync: updateUserData } = useUpdateUserData();
|
||||
return (
|
||||
<ScrollView style={{ paddingBottom: 100, flex: 1 }}>
|
||||
<View style={styles.card}>
|
||||
<Text style={styles.subTit}>Your Profile</Text>
|
||||
<View row spread paddingH-15 centerV marginV-15>
|
||||
<ImageBackground
|
||||
style={styles.pfp}
|
||||
source={require("../../../../assets/images/profile-picture.png")}
|
||||
/>
|
||||
const {mutateAsync: updateUserData} = useUpdateUserData();
|
||||
const isFirstRender = useRef(true);
|
||||
|
||||
<Text style={styles.photoSet} color="#50be0c">
|
||||
Change Photo
|
||||
</Text>
|
||||
<Text style={styles.photoSet}>Remove Photo</Text>
|
||||
</View>
|
||||
<View paddingH-15>
|
||||
<Text text80 marginT-10 marginB-7 style={styles.label}>
|
||||
First name
|
||||
</Text>
|
||||
<TextField
|
||||
text70
|
||||
placeholder="First name"
|
||||
style={styles.txtBox}
|
||||
value={firstName}
|
||||
onChangeText={async (value) => {
|
||||
setFirstName(value);
|
||||
await updateUserData({ newUserData: { firstName: value } });
|
||||
}}
|
||||
/>
|
||||
<Text text80 marginT-10 marginB-7 style={styles.label}>
|
||||
Last name
|
||||
</Text>
|
||||
<TextField
|
||||
text70
|
||||
placeholder="Last name"
|
||||
style={styles.txtBox}
|
||||
value={lastName}
|
||||
onChangeText={async (value) => {
|
||||
setLastName(value);
|
||||
await updateUserData({ newUserData: { lastName: value } });
|
||||
}}
|
||||
/>
|
||||
<Text text80 marginT-10 marginB-7 style={styles.label}>
|
||||
Email address
|
||||
</Text>
|
||||
<TextField
|
||||
text70
|
||||
placeholder="Email address"
|
||||
value={user?.email?.toString()}
|
||||
style={styles.txtBox}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View style={styles.card}>
|
||||
<Text style={styles.subTit}>Settings</Text>
|
||||
<Text text80 marginT-20 marginB-7 style={styles.label}>
|
||||
Time Zone
|
||||
</Text>
|
||||
<TextField text70 placeholder="Time Zone" style={styles.txtBox} />
|
||||
</View>
|
||||
</ScrollView>
|
||||
);
|
||||
const handleUpdateUserData = async () => {
|
||||
await updateUserData({newUserData: {firstName, lastName, timeZone}});
|
||||
}
|
||||
|
||||
const debouncedUserDataUpdate = debounce(handleUpdateUserData, 500);
|
||||
|
||||
useEffect(() => {
|
||||
if (isFirstRender.current) {
|
||||
isFirstRender.current = false;
|
||||
return;
|
||||
}
|
||||
debouncedUserDataUpdate();
|
||||
}, [timeZone, lastName, firstName]);
|
||||
|
||||
return (
|
||||
<ScrollView style={{paddingBottom: 100, flex: 1}}>
|
||||
<View style={styles.card}>
|
||||
<Text style={styles.subTit}>Your Profile</Text>
|
||||
<View row spread paddingH-15 centerV marginV-15>
|
||||
<ImageBackground
|
||||
style={styles.pfp}
|
||||
source={require("../../../../assets/images/profile-picture.png")}
|
||||
/>
|
||||
|
||||
<Text style={styles.photoSet} color="#50be0c">
|
||||
Change Photo
|
||||
</Text>
|
||||
<Text style={styles.photoSet}>Remove Photo</Text>
|
||||
</View>
|
||||
<View paddingH-15>
|
||||
<Text text80 marginT-10 marginB-7 style={styles.label}>
|
||||
First name
|
||||
</Text>
|
||||
<TextField
|
||||
text70
|
||||
placeholder="First name"
|
||||
style={styles.txtBox}
|
||||
value={firstName}
|
||||
onChangeText={async (value) => {
|
||||
setFirstName(value);
|
||||
}}
|
||||
/>
|
||||
<Text text80 marginT-10 marginB-7 style={styles.label}>
|
||||
Last name
|
||||
</Text>
|
||||
<TextField
|
||||
text70
|
||||
placeholder="Last name"
|
||||
style={styles.txtBox}
|
||||
value={lastName}
|
||||
onChangeText={async (value) => {
|
||||
setLastName(value);
|
||||
}}
|
||||
/>
|
||||
<Text text80 marginT-10 marginB-7 style={styles.label}>
|
||||
Email address
|
||||
</Text>
|
||||
<TextField
|
||||
editable={false}
|
||||
text70
|
||||
placeholder="Email address"
|
||||
value={user?.email?.toString()}
|
||||
style={styles.txtBox}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View style={styles.card}>
|
||||
<Text style={styles.subTit}>Settings</Text>
|
||||
<Text style={styles.jakarta12}>Time Zone</Text>
|
||||
<View style={styles.viewPicker}>
|
||||
<Picker
|
||||
// editable={!isLoading}
|
||||
value={timeZone}
|
||||
onChange={(item) => {
|
||||
setTimeZone(item as string)
|
||||
}}
|
||||
showSearch
|
||||
floatingPlaceholder
|
||||
style={styles.inViewPicker}
|
||||
trailingAccessory={
|
||||
<View style={{
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
height: "100%",
|
||||
marginTop: -38,
|
||||
paddingRight: 15
|
||||
}}>
|
||||
<Ionicons name={"chevron-down"} style={{alignSelf: "center"}} size={20}
|
||||
color={"#000000"}/>
|
||||
</View>
|
||||
}
|
||||
>
|
||||
{timeZoneItems}
|
||||
</Picker>
|
||||
</View>
|
||||
</View>
|
||||
</ScrollView>
|
||||
);
|
||||
};
|
||||
|
||||
const timeZoneItems = Object.keys(tz.zones).sort().map((zone) => (
|
||||
<Picker.Item key={zone} label={zone.replace("/", " / ").replace("_", " ")} value={zone}/>
|
||||
));
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
card: {
|
||||
marginVertical: 15,
|
||||
backgroundColor: "white",
|
||||
width: "100%",
|
||||
borderRadius: 12,
|
||||
paddingHorizontal: 20,
|
||||
paddingVertical: 21,
|
||||
},
|
||||
pfp: {
|
||||
aspectRatio: 1,
|
||||
width: 65.54,
|
||||
backgroundColor: "green",
|
||||
borderRadius: 20,
|
||||
},
|
||||
txtBox: {
|
||||
backgroundColor: "#fafafa",
|
||||
borderRadius: 50,
|
||||
borderWidth: 2,
|
||||
borderColor: "#cecece",
|
||||
padding: 15,
|
||||
height: 45,
|
||||
fontFamily: "PlusJakartaSans_500Medium",
|
||||
fontSize: 13
|
||||
},
|
||||
subTit: {
|
||||
fontFamily: "Manrope_500Medium",
|
||||
fontSize: 15,
|
||||
},
|
||||
label: {
|
||||
fontFamily: "PlusJakartaSans_500Medium",
|
||||
fontSize: 12,
|
||||
color: "#a1a1a1"
|
||||
},
|
||||
photoSet:{
|
||||
fontFamily: "PlusJakartaSans_500Medium",
|
||||
fontSize: 13.07
|
||||
}
|
||||
card: {
|
||||
marginVertical: 15,
|
||||
backgroundColor: "white",
|
||||
width: "100%",
|
||||
borderRadius: 12,
|
||||
paddingHorizontal: 20,
|
||||
paddingVertical: 21,
|
||||
},
|
||||
pfp: {
|
||||
aspectRatio: 1,
|
||||
width: 65.54,
|
||||
backgroundColor: "green",
|
||||
borderRadius: 20,
|
||||
},
|
||||
txtBox: {
|
||||
backgroundColor: "#fafafa",
|
||||
borderRadius: 50,
|
||||
borderWidth: 2,
|
||||
borderColor: "#cecece",
|
||||
padding: 15,
|
||||
height: 45,
|
||||
fontFamily: "PlusJakartaSans_500Medium",
|
||||
fontSize: 13
|
||||
},
|
||||
subTit: {
|
||||
fontFamily: "Manrope_500Medium",
|
||||
fontSize: 15,
|
||||
},
|
||||
label: {
|
||||
fontFamily: "PlusJakartaSans_500Medium",
|
||||
fontSize: 12,
|
||||
color: "#a1a1a1"
|
||||
},
|
||||
photoSet: {
|
||||
fontFamily: "PlusJakartaSans_500Medium",
|
||||
fontSize: 13.07
|
||||
},
|
||||
jakarta12: {
|
||||
paddingVertical: 10,
|
||||
fontFamily: "PlusJakartaSans_500Medium",
|
||||
fontSize: 12,
|
||||
color: "#a1a1a1",
|
||||
},
|
||||
picker: {
|
||||
borderRadius: 50,
|
||||
paddingVertical: 12,
|
||||
paddingHorizontal: 16,
|
||||
backgroundColor: Colors.grey80,
|
||||
marginBottom: 16,
|
||||
borderColor: Colors.grey50,
|
||||
borderWidth: 1,
|
||||
marginTop: -20,
|
||||
height: 40,
|
||||
zIndex: 10,
|
||||
},
|
||||
viewPicker: {
|
||||
borderRadius: 50,
|
||||
backgroundColor: Colors.grey80,
|
||||
marginBottom: 16,
|
||||
borderColor: Colors.grey50,
|
||||
borderWidth: 1,
|
||||
marginTop: 0,
|
||||
height: 40,
|
||||
zIndex: 10,
|
||||
},
|
||||
inViewPicker: {
|
||||
borderRadius: 50,
|
||||
paddingVertical: 12,
|
||||
paddingHorizontal: 16,
|
||||
marginBottom: 16,
|
||||
marginTop: -20,
|
||||
height: 40,
|
||||
zIndex: 10,
|
||||
},
|
||||
});
|
||||
|
||||
export default MyProfile;
|
||||
|
||||
Reference in New Issue
Block a user