mirror of
https://github.com/urosran/cally.git
synced 2025-07-16 01:56:16 +00:00
refresh button, calendar margins
This commit is contained in:
@ -1,8 +1,18 @@
|
||||
import React from "react";
|
||||
import { Drawer } from "expo-router/drawer";
|
||||
import { useSignOut } from "@/hooks/firebase/useSignOut";
|
||||
import {DrawerContentScrollView, DrawerNavigationOptions, DrawerNavigationProp} from "@react-navigation/drawer";
|
||||
import {Button, ButtonSize, Text, TouchableOpacity, View,} from "react-native-ui-lib";
|
||||
import {
|
||||
DrawerContentScrollView,
|
||||
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 NavGroceryIcon from "@/assets/svgs/NavGroceryIcon";
|
||||
@ -24,6 +34,8 @@ import {DeviceType} from "expo-device";
|
||||
import FeedbackNavIcon from "@/assets/svgs/FeedbackNavIcon";
|
||||
import DrawerIcon from "@/assets/svgs/DrawerIcon";
|
||||
import { RouteProp } from "@react-navigation/core";
|
||||
import RefreshButton from "@/components/shared/RefreshButton";
|
||||
import { useCalSync } from "@/hooks/useCalSync";
|
||||
|
||||
type DrawerParamList = {
|
||||
index: undefined;
|
||||
@ -43,12 +55,11 @@ interface HeaderRightProps {
|
||||
}
|
||||
|
||||
const MemoizedViewSwitch = React.memo<ViewSwitchProps>(({ navigation }) => (
|
||||
<View marginR-16>
|
||||
<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);
|
||||
|
||||
if (Device.deviceType !== DeviceType.TABLET || !showViewSwitch) {
|
||||
@ -56,13 +67,23 @@ const HeaderRight = React.memo<HeaderRightProps>(({routeName, navigation}) => {
|
||||
}
|
||||
|
||||
return <MemoizedViewSwitch navigation={navigation} />;
|
||||
});
|
||||
}
|
||||
);
|
||||
export default function TabLayout() {
|
||||
const { mutateAsync: signOut } = useSignOut();
|
||||
const setIsFamilyView = useSetAtom(isFamilyViewAtom);
|
||||
const setPageIndex = useSetAtom(settingsPageIndex);
|
||||
const setUserView = useSetAtom(userSettingsView);
|
||||
const setToDosIndex = useSetAtom(toDosPageIndex);
|
||||
const { resyncAllCalendars, isSyncing } = useCalSync();
|
||||
|
||||
const onRefresh = React.useCallback(async () => {
|
||||
try {
|
||||
await resyncAllCalendars();
|
||||
} catch (error) {
|
||||
console.error("Refresh failed:", error);
|
||||
}
|
||||
}, [resyncAllCalendars]);
|
||||
|
||||
const screenOptions = ({
|
||||
navigation,
|
||||
@ -72,7 +93,8 @@ export default function TabLayout() {
|
||||
route: RouteProp<DrawerParamList>;
|
||||
}): DrawerNavigationOptions => ({
|
||||
headerShown: true,
|
||||
headerTitleAlign: Device.deviceType === DeviceType.TABLET ? "left" : "center",
|
||||
headerTitleAlign:
|
||||
Device.deviceType === DeviceType.TABLET ? "left" : "center",
|
||||
headerTitleStyle: {
|
||||
fontFamily: "Manrope_600SemiBold",
|
||||
fontSize: Device.deviceType === DeviceType.TABLET ? 22 : 17,
|
||||
@ -86,20 +108,31 @@ export default function TabLayout() {
|
||||
</TouchableOpacity>
|
||||
),
|
||||
headerRight: () => {
|
||||
const showViewSwitch = ["calendar", "todos", "index"].includes(route.name);
|
||||
const showViewSwitch = ["calendar", "todos", "index"].includes(
|
||||
route.name
|
||||
);
|
||||
|
||||
if (Device.deviceType !== DeviceType.TABLET || !showViewSwitch) {
|
||||
return null;
|
||||
return (
|
||||
<View marginR-16>
|
||||
<RefreshButton onRefresh={onRefresh} isSyncing={isSyncing} />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
return <MemoizedViewSwitch navigation={navigation}/>;
|
||||
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
|
||||
|
@ -61,7 +61,6 @@ export default function Screen() {
|
||||
justifyContent: "center",
|
||||
paddingRight: 200,
|
||||
}}
|
||||
refreshControl={refreshControl}
|
||||
bounces={true}
|
||||
showsVerticalScrollIndicator={false}
|
||||
pointerEvents={isSyncing ? "auto" : "none"}
|
||||
@ -74,7 +73,6 @@ export default function Screen() {
|
||||
<ScrollView
|
||||
style={{flex: 1, height: "100%"}}
|
||||
contentContainerStyle={{flex: 1, height: "100%"}}
|
||||
refreshControl={refreshControl}
|
||||
bounces={true}
|
||||
showsVerticalScrollIndicator={false}
|
||||
>
|
||||
|
20
assets/svgs/CheckmarkIcon.tsx
Normal file
20
assets/svgs/CheckmarkIcon.tsx
Normal 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
|
@ -5,9 +5,9 @@ import { InnerCalendar } from "@/components/pages/calendar/InnerCalendar";
|
||||
export default function CalendarPage() {
|
||||
return (
|
||||
<View
|
||||
style={{ flex: 1, height: "100%", padding: 10 }}
|
||||
paddingH-22
|
||||
paddingT-22
|
||||
style={{ flex: 1, height: "100%", padding: 0 }}
|
||||
paddingH-0
|
||||
paddingT-0
|
||||
>
|
||||
{/*<HeaderTemplate
|
||||
message={"Let's get your week started !"}
|
||||
|
@ -25,9 +25,10 @@ export const InnerCalendar = () => {
|
||||
return (
|
||||
<>
|
||||
<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}
|
||||
onLayout={onLayout}
|
||||
paddingB-15
|
||||
>
|
||||
<CalendarHeader/>
|
||||
{calendarHeight > 0 && (
|
||||
|
67
components/shared/RefreshButton.tsx
Normal file
67
components/shared/RefreshButton.tsx
Normal 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;
|
Reference in New Issue
Block a user