refresh button, calendar margins

This commit is contained in:
ivic00
2024-12-05 11:32:54 +01:00
parent 1b288d095f
commit 8526d79ba1
6 changed files with 436 additions and 317 deletions

View File

@ -1,9 +1,19 @@
import React from "react"; import React from "react";
import {Drawer} from "expo-router/drawer"; import { Drawer } from "expo-router/drawer";
import {useSignOut} from "@/hooks/firebase/useSignOut"; import { useSignOut } from "@/hooks/firebase/useSignOut";
import {DrawerContentScrollView, DrawerNavigationOptions, DrawerNavigationProp} from "@react-navigation/drawer"; import {
import {Button, ButtonSize, Text, TouchableOpacity, View,} from "react-native-ui-lib"; DrawerContentScrollView,
import {ImageBackground, StyleSheet} from "react-native"; DrawerNavigationOptions,
DrawerNavigationProp,
} from "@react-navigation/drawer";
import {
Button,
ButtonSize,
Text,
TouchableOpacity,
View,
} from "react-native-ui-lib";
import { ImageBackground, StyleSheet } from "react-native";
import DrawerButton from "@/components/shared/DrawerButton"; import DrawerButton from "@/components/shared/DrawerButton";
import NavGroceryIcon from "@/assets/svgs/NavGroceryIcon"; import NavGroceryIcon from "@/assets/svgs/NavGroceryIcon";
import NavToDosIcon from "@/assets/svgs/NavToDosIcon"; import NavToDosIcon from "@/assets/svgs/NavToDosIcon";
@ -11,165 +21,188 @@ 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 ViewSwitch from "@/components/pages/(tablet_pages)/ViewSwitch"; import ViewSwitch from "@/components/pages/(tablet_pages)/ViewSwitch";
import {useSetAtom} from "jotai"; import { useSetAtom } from "jotai";
import { import {
isFamilyViewAtom, isFamilyViewAtom,
settingsPageIndex, settingsPageIndex,
toDosPageIndex, toDosPageIndex,
userSettingsView, userSettingsView,
} from "@/components/pages/calendar/atoms"; } from "@/components/pages/calendar/atoms";
import Ionicons from "@expo/vector-icons/Ionicons"; import Ionicons from "@expo/vector-icons/Ionicons";
import * as Device from "expo-device"; import * as Device from "expo-device";
import {DeviceType} from "expo-device"; import { DeviceType } from "expo-device";
import FeedbackNavIcon from "@/assets/svgs/FeedbackNavIcon"; import FeedbackNavIcon from "@/assets/svgs/FeedbackNavIcon";
import DrawerIcon from "@/assets/svgs/DrawerIcon"; import DrawerIcon from "@/assets/svgs/DrawerIcon";
import {RouteProp} from "@react-navigation/core"; import { RouteProp } from "@react-navigation/core";
import RefreshButton from "@/components/shared/RefreshButton";
import { useCalSync } from "@/hooks/useCalSync";
type DrawerParamList = { type DrawerParamList = {
index: undefined; index: undefined;
calendar: undefined; calendar: undefined;
todos: undefined; todos: undefined;
}; };
type NavigationProp = DrawerNavigationProp<DrawerParamList>; type NavigationProp = DrawerNavigationProp<DrawerParamList>;
interface ViewSwitchProps { interface ViewSwitchProps {
navigation: NavigationProp; navigation: NavigationProp;
} }
interface HeaderRightProps { interface HeaderRightProps {
routeName: keyof DrawerParamList; routeName: keyof DrawerParamList;
navigation: NavigationProp; navigation: NavigationProp;
} }
const MemoizedViewSwitch = React.memo<ViewSwitchProps>(({navigation}) => ( const MemoizedViewSwitch = React.memo<ViewSwitchProps>(({ navigation }) => (
<View marginR-16> <ViewSwitch navigation={navigation} />
<ViewSwitch navigation={navigation}/>
</View>
)); ));
const HeaderRight = React.memo<HeaderRightProps>(({routeName, navigation}) => { const HeaderRight = React.memo<HeaderRightProps>(
({ routeName, navigation }) => {
const showViewSwitch = ["calendar", "todos", "index"].includes(routeName); const showViewSwitch = ["calendar", "todos", "index"].includes(routeName);
if (Device.deviceType !== DeviceType.TABLET || !showViewSwitch) { if (Device.deviceType !== DeviceType.TABLET || !showViewSwitch) {
return null; return null;
} }
return <MemoizedViewSwitch navigation={navigation}/>; return <MemoizedViewSwitch navigation={navigation} />;
}); }
);
export default function TabLayout() { export default function TabLayout() {
const {mutateAsync: signOut} = useSignOut(); const { mutateAsync: signOut } = useSignOut();
const setIsFamilyView = useSetAtom(isFamilyViewAtom); const setIsFamilyView = useSetAtom(isFamilyViewAtom);
const setPageIndex = useSetAtom(settingsPageIndex); const setPageIndex = useSetAtom(settingsPageIndex);
const setUserView = useSetAtom(userSettingsView); const setUserView = useSetAtom(userSettingsView);
const setToDosIndex = useSetAtom(toDosPageIndex); const setToDosIndex = useSetAtom(toDosPageIndex);
const { resyncAllCalendars, isSyncing } = useCalSync();
const screenOptions = ({ const onRefresh = React.useCallback(async () => {
navigation, try {
route, await resyncAllCalendars();
}: { } catch (error) {
navigation: DrawerNavigationProp<DrawerParamList>; console.error("Refresh failed:", error);
route: RouteProp<DrawerParamList>; }
}): DrawerNavigationOptions => ({ }, [resyncAllCalendars]);
headerShown: true,
headerTitleAlign: Device.deviceType === DeviceType.TABLET ? "left" : "center", const screenOptions = ({
headerTitleStyle: { navigation,
fontFamily: "Manrope_600SemiBold", route,
fontSize: Device.deviceType === DeviceType.TABLET ? 22 : 17, }: {
}, navigation: DrawerNavigationProp<DrawerParamList>;
headerLeft: () => ( route: RouteProp<DrawerParamList>;
<TouchableOpacity }): DrawerNavigationOptions => ({
onPress={navigation.toggleDrawer} headerShown: true,
style={{marginLeft: 16}} headerTitleAlign:
Device.deviceType === DeviceType.TABLET ? "left" : "center",
headerTitleStyle: {
fontFamily: "Manrope_600SemiBold",
fontSize: Device.deviceType === DeviceType.TABLET ? 22 : 17,
},
headerLeft: () => (
<TouchableOpacity
onPress={navigation.toggleDrawer}
style={{ marginLeft: 16 }}
>
<DrawerIcon />
</TouchableOpacity>
),
headerRight: () => {
const showViewSwitch = ["calendar", "todos", "index"].includes(
route.name
);
if (Device.deviceType !== DeviceType.TABLET || !showViewSwitch) {
return (
<View marginR-16>
<RefreshButton onRefresh={onRefresh} isSyncing={isSyncing} />
</View>
);
}
return (
<View marginR-16 row>
<RefreshButton onRefresh={onRefresh} isSyncing={isSyncing} />
<MemoizedViewSwitch navigation={navigation} />
</View>
);
},
drawerStyle: {
width: Device.deviceType === DeviceType.TABLET ? "30%" : "90%",
backgroundColor: "#f9f8f7",
height: "100%",
},
});
return (
<Drawer
initialRouteName={"index"}
detachInactiveScreens
screenOptions={screenOptions}
drawerContent={(props) => {
return (
<DrawerContentScrollView {...props} style={{}}>
<View centerV marginH-30 marginT-20 marginB-20 row>
<ImageBackground
source={require("../../assets/images/splash.png")}
style={{
backgroundColor: "transparent",
height: 51.43,
aspectRatio: 1,
marginRight: 8,
}}
/>
<Text style={styles.title}>Welcome to Cally</Text>
</View>
<View
style={{
flexDirection: "row",
paddingHorizontal: 30,
}}
> >
<DrawerIcon/> <View style={{ flex: 1, paddingRight: 5 }}>
</TouchableOpacity> <DrawerButton
), title={"Calendar"}
headerRight: () => { color="rgb(7, 184, 199)"
const showViewSwitch = ["calendar", "todos", "index"].includes(route.name); bgColor={"rgb(231, 248, 250)"}
pressFunc={() => {
if (Device.deviceType !== DeviceType.TABLET || !showViewSwitch) { props.navigation.navigate("calendar");
return null; setPageIndex(0);
} setToDosIndex(0);
setUserView(true);
return <MemoizedViewSwitch navigation={navigation}/>; setIsFamilyView(false);
}, }}
drawerStyle: { icon={<NavCalendarIcon />}
width: Device.deviceType === DeviceType.TABLET ? "30%" : "90%", />
backgroundColor: "#f9f8f7", <DrawerButton
height: "100%", color="#50be0c"
}, title={"Groceries"}
}) bgColor={"#eef9e7"}
pressFunc={() => {
return ( props.navigation.navigate("grocery");
<Drawer setPageIndex(0);
initialRouteName={"index"} setToDosIndex(0);
detachInactiveScreens setUserView(true);
screenOptions={screenOptions} setIsFamilyView(false);
drawerContent={(props) => { }}
return ( icon={<NavGroceryIcon />}
<DrawerContentScrollView {...props} style={{}}> />
<View centerV marginH-30 marginT-20 marginB-20 row> <DrawerButton
<ImageBackground color="#ea156d"
source={require("../../assets/images/splash.png")} title={"Feedback"}
style={{ bgColor={"#fdedf4"}
backgroundColor: "transparent", pressFunc={() => {
height: 51.43, props.navigation.navigate("feedback");
aspectRatio: 1, setPageIndex(0);
marginRight: 8, setToDosIndex(0);
}} setUserView(true);
/> setIsFamilyView(false);
<Text style={styles.title}>Welcome to Cally</Text> }}
</View> icon={<FeedbackNavIcon />}
<View />
style={{ </View>
flexDirection: "row", <View style={{ flex: 1, paddingRight: 0 }}>
paddingHorizontal: 30, {/*<DrawerButton
}}
>
<View style={{flex: 1, paddingRight: 5}}>
<DrawerButton
title={"Calendar"}
color="rgb(7, 184, 199)"
bgColor={"rgb(231, 248, 250)"}
pressFunc={() => {
props.navigation.navigate("calendar");
setPageIndex(0);
setToDosIndex(0);
setUserView(true);
setIsFamilyView(false);
}}
icon={<NavCalendarIcon/>}
/>
<DrawerButton
color="#50be0c"
title={"Groceries"}
bgColor={"#eef9e7"}
pressFunc={() => {
props.navigation.navigate("grocery");
setPageIndex(0);
setToDosIndex(0);
setUserView(true);
setIsFamilyView(false);
}}
icon={<NavGroceryIcon/>}
/>
<DrawerButton
color="#ea156d"
title={"Feedback"}
bgColor={"#fdedf4"}
pressFunc={() => {
props.navigation.navigate("feedback");
setPageIndex(0);
setToDosIndex(0);
setUserView(true);
setIsFamilyView(false);
}}
icon={<FeedbackNavIcon/>}
/>
</View>
<View style={{flex: 1, paddingRight: 0}}>
{/*<DrawerButton
color="#fd1775" color="#fd1775"
title={"My Reminders"} title={"My Reminders"}
bgColor={"#ffe8f2"} bgColor={"#ffe8f2"}
@ -182,184 +215,184 @@ export default function TabLayout() {
/> />
} }
/>*/} />*/}
<DrawerButton <DrawerButton
color="#8005eb" color="#8005eb"
title={"To Do's"} title={"To Do's"}
bgColor={"#f3e6fd"} bgColor={"#f3e6fd"}
pressFunc={() => { pressFunc={() => {
props.navigation.navigate("todos"); props.navigation.navigate("todos");
setPageIndex(0); setPageIndex(0);
setToDosIndex(0); setToDosIndex(0);
setUserView(true); setUserView(true);
setIsFamilyView(false); setIsFamilyView(false);
}} }}
icon={<NavToDosIcon/>} icon={<NavToDosIcon />}
/> />
<DrawerButton <DrawerButton
color="#e0ca03" color="#e0ca03"
title={"Brain Dump"} title={"Brain Dump"}
bgColor={"#fffacb"} bgColor={"#fffacb"}
pressFunc={() => { pressFunc={() => {
props.navigation.navigate("brain_dump"); props.navigation.navigate("brain_dump");
setPageIndex(0); setPageIndex(0);
setToDosIndex(0); setToDosIndex(0);
setUserView(true); setUserView(true);
setIsFamilyView(false); setIsFamilyView(false);
}} }}
icon={<NavBrainDumpIcon/>} icon={<NavBrainDumpIcon />}
/> />
<DrawerButton <DrawerButton
color="#e0ca03" color="#e0ca03"
title={"Notifications"} title={"Notifications"}
bgColor={"#ffdda1"} bgColor={"#ffdda1"}
pressFunc={() => { pressFunc={() => {
props.navigation.navigate("notifications"); props.navigation.navigate("notifications");
setPageIndex(0); setPageIndex(0);
setToDosIndex(0); setToDosIndex(0);
setUserView(true); setUserView(true);
setIsFamilyView(false); setIsFamilyView(false);
}} }}
icon={ icon={
<Ionicons <Ionicons
name="notifications-outline" name="notifications-outline"
size={24} size={24}
color={"#ffa200"} color={"#ffa200"}
/> />
} }
/> />
</View> </View>
</View> </View>
<Button <Button
onPress={() => { onPress={() => {
props.navigation.navigate("settings"); props.navigation.navigate("settings");
setPageIndex(0); setPageIndex(0);
setToDosIndex(0); setToDosIndex(0);
setUserView(true); setUserView(true);
setIsFamilyView(false); setIsFamilyView(false);
}} }}
label={"Manage Settings"} label={"Manage Settings"}
labelStyle={styles.label} labelStyle={styles.label}
iconSource={() => ( iconSource={() => (
<View <View
backgroundColor="#ededed" backgroundColor="#ededed"
width={60} width={60}
height={60} height={60}
style={{borderRadius: 50}} style={{ borderRadius: 50 }}
marginR-10 marginR-10
centerV centerV
centerH centerH
> >
<NavSettingsIcon/> <NavSettingsIcon />
</View> </View>
)} )}
backgroundColor="white" backgroundColor="white"
color="#464039" color="#464039"
paddingV-30 paddingV-30
marginH-30 marginH-30
borderRadius={18.55} borderRadius={18.55}
style={{elevation: 0}} style={{ elevation: 0 }}
/> />
<Button <Button
size={ButtonSize.large} size={ButtonSize.large}
marginH-10 marginH-10
marginT-12 marginT-12
paddingV-15 paddingV-15
style={{ style={{
marginTop: 50, marginTop: 50,
backgroundColor: "transparent", backgroundColor: "transparent",
borderWidth: 1.3, borderWidth: 1.3,
borderColor: "#fd1775", borderColor: "#fd1775",
}} }}
label="Sign out of Cally" label="Sign out of Cally"
color="#fd1775" color="#fd1775"
labelStyle={styles.signOut} labelStyle={styles.signOut}
onPress={() => signOut()} onPress={() => signOut()}
/>
</DrawerContentScrollView>
);
}}
>
<Drawer.Screen
name="index"
options={{
drawerLabel: "Calendar",
title:
Device.deviceType === DeviceType.TABLET
? "Family Calendar"
: "Calendar",
}}
/> />
<Drawer.Screen </DrawerContentScrollView>
name="calendar" );
options={{ }}
drawerLabel: "Calendar", >
title: <Drawer.Screen
Device.deviceType === DeviceType.TABLET name="index"
? "Family Calendar" options={{
: "Calendar", drawerLabel: "Calendar",
drawerItemStyle: {display: "none"}, title:
}} Device.deviceType === DeviceType.TABLET
/> ? "Family Calendar"
<Drawer.Screen : "Calendar",
name="brain_dump" }}
options={{ />
drawerLabel: "Brain Dump", <Drawer.Screen
title: "Brain Dump", name="calendar"
}} options={{
/> drawerLabel: "Calendar",
<Drawer.Screen title:
name="settings" Device.deviceType === DeviceType.TABLET
options={{ ? "Family Calendar"
drawerLabel: "Settings", : "Calendar",
title: "Settings", drawerItemStyle: { display: "none" },
}} }}
/> />
<Drawer.Screen <Drawer.Screen
name="grocery" name="brain_dump"
options={{ options={{
drawerLabel: "Grocery", drawerLabel: "Brain Dump",
title: "Grocery", title: "Brain Dump",
}} }}
/> />
<Drawer.Screen <Drawer.Screen
name="reminders" name="settings"
options={{ options={{
drawerLabel: "Reminders", drawerLabel: "Settings",
title: "Reminders", title: "Settings",
}} }}
/> />
<Drawer.Screen <Drawer.Screen
name="todos" name="grocery"
options={{ options={{
drawerLabel: "To-Do", drawerLabel: "Grocery",
title: title: "Grocery",
Device.deviceType === DeviceType.TABLET }}
? "Family To Do's" />
: "To Do's", <Drawer.Screen
}} name="reminders"
/> options={{
<Drawer.Screen drawerLabel: "Reminders",
name="notifications" title: "Reminders",
options={{ }}
drawerLabel: "Notifications", />
title: "Notifications", <Drawer.Screen
}} name="todos"
/> options={{
<Drawer.Screen drawerLabel: "To-Do",
name="feedback" title:
options={{drawerLabel: "Feedback", title: "Feedback"}} Device.deviceType === DeviceType.TABLET
/> ? "Family To Do's"
</Drawer> : "To Do's",
); }}
/>
<Drawer.Screen
name="notifications"
options={{
drawerLabel: "Notifications",
title: "Notifications",
}}
/>
<Drawer.Screen
name="feedback"
options={{ drawerLabel: "Feedback", title: "Feedback" }}
/>
</Drawer>
);
} }
const styles = StyleSheet.create({ const styles = StyleSheet.create({
signOut: {fontFamily: "Poppins_500Medium", fontSize: 15}, signOut: { fontFamily: "Poppins_500Medium", fontSize: 15 },
label: {fontFamily: "Poppins_400Medium", fontSize: 15}, label: { fontFamily: "Poppins_400Medium", fontSize: 15 },
title: { title: {
fontSize: 26.13, fontSize: 26.13,
fontFamily: "Manrope_600SemiBold", fontFamily: "Manrope_600SemiBold",
color: "#262627", color: "#262627",
}, },
}); });

View File

@ -61,7 +61,6 @@ export default function Screen() {
justifyContent: "center", justifyContent: "center",
paddingRight: 200, paddingRight: 200,
}} }}
refreshControl={refreshControl}
bounces={true} bounces={true}
showsVerticalScrollIndicator={false} showsVerticalScrollIndicator={false}
pointerEvents={isSyncing ? "auto" : "none"} pointerEvents={isSyncing ? "auto" : "none"}
@ -74,7 +73,6 @@ export default function Screen() {
<ScrollView <ScrollView
style={{flex: 1, height: "100%"}} style={{flex: 1, height: "100%"}}
contentContainerStyle={{flex: 1, height: "100%"}} contentContainerStyle={{flex: 1, height: "100%"}}
refreshControl={refreshControl}
bounces={true} bounces={true}
showsVerticalScrollIndicator={false} showsVerticalScrollIndicator={false}
> >

View File

@ -0,0 +1,20 @@
import * as React from "react"
import Svg, { SvgProps, Path } from "react-native-svg"
const CheckmarkIcon = (props: SvgProps) => (
<Svg
width={13}
height={10}
viewBox="0 0 13 10"
fill={props.color || "white"}
{...props}
>
<Path
stroke="#fff"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={1.95}
d="m1.48 5.489 3.2 3.178 7.2-7.15"
/>
</Svg>
)
export default CheckmarkIcon

View File

@ -5,9 +5,9 @@ import { InnerCalendar } from "@/components/pages/calendar/InnerCalendar";
export default function CalendarPage() { export default function CalendarPage() {
return ( return (
<View <View
style={{ flex: 1, height: "100%", padding: 10 }} style={{ flex: 1, height: "100%", padding: 0 }}
paddingH-22 paddingH-0
paddingT-22 paddingT-0
> >
{/*<HeaderTemplate {/*<HeaderTemplate
message={"Let's get your week started !"} message={"Let's get your week started !"}

View File

@ -25,9 +25,10 @@ export const InnerCalendar = () => {
return ( return (
<> <>
<View <View
style={{flex: 1, backgroundColor: "#fff", borderRadius: 30, marginBottom: 10, overflow: "hidden"}} style={{flex: 1, backgroundColor: "#fff", borderRadius: 0, marginBottom: 0, overflow: "hidden"}}
ref={calendarContainerRef} ref={calendarContainerRef}
onLayout={onLayout} onLayout={onLayout}
paddingB-15
> >
<CalendarHeader/> <CalendarHeader/>
{calendarHeight > 0 && ( {calendarHeight > 0 && (

View File

@ -0,0 +1,67 @@
import React, { useRef, useEffect } from 'react';
import { TouchableOpacity, Animated, Easing } from 'react-native';
import { Feather } from '@expo/vector-icons';
interface RefreshButtonProps {
onRefresh: () => Promise<void>;
isSyncing: boolean;
size?: number;
color?: string;
}
const RefreshButton = ({
onRefresh,
isSyncing,
size = 24,
color = "#83807F"
}: RefreshButtonProps) => {
const rotateAnim = useRef(new Animated.Value(0)).current;
const rotationLoop = useRef<Animated.CompositeAnimation | null>(null);
useEffect(() => {
if (isSyncing) {
startContinuousRotation();
} else {
stopRotation();
}
}, [isSyncing]);
const startContinuousRotation = () => {
rotateAnim.setValue(0);
rotationLoop.current = Animated.loop(
Animated.timing(rotateAnim, {
toValue: 1,
duration: 1000,
easing: Easing.linear,
useNativeDriver: true,
})
);
rotationLoop.current.start();
};
const stopRotation = () => {
rotationLoop.current?.stop();
rotateAnim.setValue(0);
};
const rotate = rotateAnim.interpolate({
inputRange: [0, 1],
outputRange: ['0deg', '360deg'],
});
const handlePress = async () => {
if (!isSyncing) {
await onRefresh();
}
};
return (
<TouchableOpacity onPress={handlePress} disabled={isSyncing}>
<Animated.View style={{ transform: [{ rotate }] }}>
<Feather name="refresh-cw" size={size} color={color} />
</Animated.View>
</TouchableOpacity>
);
};
export default RefreshButton;