Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/task progress #70

Merged
merged 4 commits into from
Dec 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions client-new/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ import { NativeBaseProvider, extendTheme } from 'native-base';
import React from 'react';
import { StyleSheet } from 'react-native';
import { SafeAreaProvider } from 'react-native-safe-area-context';
import { LogBox } from 'react-native';

// Ignore log notification by message:
// LogBox.ignoreLogs(['Warning: ...']); // Ignore log notification by message
// LogBox.ignoreAllLogs();//Ignore all log notifications

const queryClient = new QueryClient({
defaultOptions: {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
import { ITask } from '@/interfaces/ITask';
import { fetchTaskTag } from '@/services/TaskService';
import { fetchTaskTag, getTaskProgress } from '@/services/TaskService';
import { Text, View } from 'native-base';

import React, { useEffect, useState } from 'react';
import React, { useCallback, useEffect, useState } from 'react';
import { Pressable, TouchableOpacity } from 'react-native';

import RightArrowIcon from '../icons/RightArrowIcon';
import CircleProgress from '../reusable/CircleProgress';
import { useQuery } from '@tanstack/react-query';
import { useUser } from '@/contexts/UserContext';
import { useIsFocused } from '@react-navigation/native';
import { color } from 'native-base/lib/typescript/theme/styled-system';
import {
heightPercentageToDP as h,
widthPercentageToDP as w
} from 'react-native-responsive-screen';


type HSTCProps = {
task: ITask;
Expand All @@ -15,10 +24,28 @@ type HSTCProps = {
};

const HomeScreenTaskCard: React.FC<HSTCProps> = ({ task, isAllTasks, handleOnPress }) => {
const { user } = useUser();
const isFocused = useIsFocused();
const [tag, setTag] = useState<string | null>(null);
const [isPending, setIsPending] = useState<boolean>(false);
const [error, setError] = useState<Error | null>(null);

const { isLoading, error: completeError, data: complete, refetch } = useQuery({
queryKey: ['fetchTaskProgress', user?.id, task?.id],
queryFn: () => getTaskProgress(user?.id, task?.id)
});

const refreshData = useCallback(async () => {
await refetch();
}, [refetch]);


useEffect(() => {
if (isFocused) {
refreshData();
}
}, [isFocused, refetch]);

useEffect(() => {
if (!isAllTasks) {
const fetchData = async () => {
Expand All @@ -37,10 +64,19 @@ const HomeScreenTaskCard: React.FC<HSTCProps> = ({ task, isAllTasks, handleOnPre
}
}, [isAllTasks, task.id]);

const progress = Math.floor(Math.random() * 100) + 1;
if (isLoading) {
return <Text>Loading...</Text>
};

if (completeError) {
return <Text>Error</Text>
};

return (
<TouchableOpacity onPress={handleOnPress}>
<TouchableOpacity
onPress={handleOnPress}
disabled={complete?.progress === 100}
>
<View
paddingLeft={5}
paddingTop={6}
Expand Down Expand Up @@ -76,19 +112,18 @@ const HomeScreenTaskCard: React.FC<HSTCProps> = ({ task, isAllTasks, handleOnPre
fontWeight: '600',
marginBottom: 5
}}
color={complete?.progress === 100 ? '#00000033' : '#2F1D12'}
>
{task.task_name}
</Text>
<Text
style={{
fontSize: 14,
color: '#2F1D12'
}}
fontSize={h('1.7%')}
color={complete?.progress === 100 ? '#00000033' : '#2F1D12'}
>
{task.task_description}
</Text>
</View>
<CircleProgress progress={progress} />
<CircleProgress task={task} />
<View
style={{
alignSelf: 'flex-end',
Expand Down
10 changes: 7 additions & 3 deletions client-new/src/components/icons/RightArrowIcon.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
import { View } from 'native-base';

import * as React from 'react';
import {
heightPercentageToDP as h,
widthPercentageToDP as w
} from 'react-native-responsive-screen';
import Svg, { Path } from 'react-native-svg';

const RightArrowIcon = () => (
type RightArrowIconProps = {
color?: string;
};

const RightArrowIcon = ({ color }: RightArrowIconProps) => (
<View width={w('8%')}>
<Svg width="24" height="25" viewBox="0 0 24 25" fill="none">
<Path
d="M10.2249 18.7606L15.5179 13.4676C15.8929 13.0925 16.1036 12.5839 16.1036 12.0536C16.1036 11.5232 15.8929 11.0146 15.5179 10.6396L10.2249 5.34656L8.81093 6.76056L14.0999 12.0536L8.80693 17.3466L10.2249 18.7606Z"
fill="#374957"
fill={color ? color : '#0F4D3F'}
/>
</Svg>
</View>
);

export default RightArrowIcon;
75 changes: 44 additions & 31 deletions client-new/src/components/reusable/CircleProgress.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,50 @@
import React, { useEffect, useRef } from 'react';
import { Animated, Text, View } from 'react-native';
import { useUser } from '@/contexts/UserContext';
import { ITask } from '@/interfaces/ITask';
import { getTaskProgress } from '@/services/TaskService';
import { useQuery } from '@tanstack/react-query';
import React, { useEffect, useRef, useState } from 'react';
import { Animated } from 'react-native';
import Svg, { Circle } from 'react-native-svg';
import { Text, View } from 'native-base';

const AnimatedCircle = Animated.createAnimatedComponent(Circle);

const CircleProgress = ({ progress }) => {
const animatedValue = useRef(new Animated.Value(0)).current;
type CircleProgressProps = {
task: ITask;
};

const CircleProgress = ({ task }: CircleProgressProps) => {
const { user } = useUser();

const { isLoading, error, data: progress, refetch } = useQuery({
queryKey: ['fetchTaskProgress', task?.id],
queryFn: () => getTaskProgress(user?.id, task?.id)
});

const strokeWidth = 10;
const animatedValue = useRef(new Animated.Value(0)).current;
const strokeWidth = 13;
const radius = 50 - strokeWidth / 2;
const circumference = 2 * Math.PI * radius;
const progressStrokeDashoffset = ((progress / 100) * circumference) / 100;

useEffect(() => {
Animated.timing(animatedValue, {
toValue: progress,
duration: 1000,
useNativeDriver: true
}).start();
}, [animatedValue, progress]);
if (progress?.progress !== undefined) {
Animated.timing(animatedValue, {
toValue: progress.progress || 0,
duration: 1000,
useNativeDriver: true
}).start();
}
}, [animatedValue, progress?.progress]);

const progressStrokeDashoffset = animatedValue.interpolate({
inputRange: [0, 100],
outputRange: [circumference, 0]
});

const { left, top } =
progress < 10
? { left: 26, top: 27 }
: progress >= 100
? { left: 17, top: 27 }
: { left: 20, top: 27 };
const textPosition = {
left: 43 - (progress?.progress < 10 ? 6 : 10),
top: 42 - (progress?.progress < 10 ? 6 : 10)
};
return (
<View style={{ alignItems: 'center', justifyContent: 'center' }}>
<Svg height="70" width="70" viewBox="0 0 100 100">
Expand All @@ -45,26 +64,20 @@ const CircleProgress = ({ progress }) => {
r={radius}
stroke="#43A573"
strokeWidth={strokeWidth}
strokeDasharray={circumference}
strokeDashoffset={animatedValue.interpolate({
inputRange: [0, 100],
outputRange: [circumference, progressStrokeDashoffset]
})}
strokeDasharray={`${circumference} ${circumference}`}
strokeDashoffset={progressStrokeDashoffset}
strokeLinecap="round"
fill="none"
transform="rotate(-90 50 50)"
/>
</Svg>
<Text
style={{
position: 'absolute',
top: top,
left: left,
zIndex: 2,
fontSize: 15
}}
position={'absolute'}
left={`${textPosition.left}%`}
top={`${textPosition.top}%`}
color={progress?.progress === 100 ? '#00000033' : '#2F1D12'}
>
{`${progress}%`}
{progress?.progress || 0}%
</Text>
</View>
);
Expand Down
92 changes: 60 additions & 32 deletions client-new/src/components/reusable/CircleProgressSubtask.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,66 @@
import React, { useEffect, useRef } from 'react';
import { Animated, Text, View } from 'react-native';
import { useUser } from '@/contexts/UserContext';
import { ITask } from '@/interfaces/ITask';
import { getTaskProgress } from '@/services/TaskService';
import { useQuery } from '@tanstack/react-query';
import React, { useCallback, useEffect, useRef, useState } from 'react';
import { Animated } from 'react-native';
import Svg, { Circle } from 'react-native-svg';
import { Text, View } from 'native-base';
import { useIsFocused } from '@react-navigation/native'; // Import useIsFocused hook

const AnimatedCircle = Animated.createAnimatedComponent(Circle);

const CircleProgress = ({ progress }) => {
const animatedValue = useRef(new Animated.Value(0)).current;
type CircleProgressProps = {
task: ITask;
};

const CircleProgress = ({ task }: CircleProgressProps) => {
const { user } = useUser();
const isFocused = useIsFocused(); // Hook to check if screen is focused

const { isLoading, error, data: progress, refetch } = useQuery({
queryKey: ['fetchTaskProgress', task?.id],
queryFn: () => getTaskProgress(user?.id, task?.id)
});


const refreshData = useCallback(async () => {
console.log(progress)
await refetch();
}, [refetch]);


useEffect(() => {
if (isFocused) {
refreshData();
}
}, [isFocused, refetch]);

const animatedValue = useRef(new Animated.Value(0)).current;
const strokeWidth = 13;
const radius = 50 - strokeWidth / 2;
const circumference = 2 * Math.PI * radius;
const progressStrokeDashoffset = ((progress / 100) * circumference) / 100;

useEffect(() => {
Animated.timing(animatedValue, {
toValue: progress,
duration: 1000,
useNativeDriver: true
}).start();
}, [animatedValue, progress]);
if (progress?.progress !== undefined) {
Animated.timing(animatedValue, {
toValue: progress.progress || 0,
duration: 1000,
useNativeDriver: true
}).start();
}
}, [animatedValue, progress?.progress]);

const progressStrokeDashoffset = animatedValue.interpolate({
inputRange: [0, 100],
outputRange: [circumference, 0]
});

const textPosition = {
left: 55 - (progress?.progress < 10 ? 6 : 10),
top: 45 - (progress?.progress < 10 ? 6 : 10)
};

const { left, top } =
progress < 10
? { left: 165, top: 41 }
: progress >= 100
? { left: 156, top: 41 }
: { left: 159, top: 41 };
return (
<View style={{ alignItems: 'center', justifyContent: 'center' }}>
<Svg height="110" width="110" viewBox="0 0 100 100">
Expand All @@ -37,7 +72,7 @@ const CircleProgress = ({ progress }) => {
strokeWidth={strokeWidth}
strokeDasharray={circumference}
strokeLinecap="round"
fill="transparent"
fill="transparent"
/>
<AnimatedCircle
cx="50"
Expand All @@ -46,30 +81,23 @@ const CircleProgress = ({ progress }) => {
stroke="#43A573"
strokeWidth={strokeWidth}
strokeDasharray={circumference}
strokeDashoffset={animatedValue.interpolate({
inputRange: [0, 100],
outputRange: [circumference, progressStrokeDashoffset]
})}
strokeDashoffset={progressStrokeDashoffset}
strokeLinecap="round"
fill="transparent"
transform="rotate(-90 50 50)"
/>
</Svg>
<Text
style={{
position: 'absolute',
top: top,
left: left,
zIndex: 2,
fontSize: 20
}}
position={'absolute'}
left={`${textPosition.left}%`}
top={`${textPosition.top}%`}
fontSize={18}
fontWeight={'600'}
>
{`${progress}%`}
{progress?.progress || 0}%
</Text>
</View>
);
};

export default CircleProgress;

//style={{ position: 'absolute', top: top, left: left, zIndex: 2, fontSize: 15 }}
Loading
Loading