diff --git a/app/index.tsx b/app/index.tsx index 57f7c798bc44851d095a42b179d79379ee26c2b6..705c44aa000c1e8b40f0ffe596bb66dae1de0aee 100644 --- a/app/index.tsx +++ b/app/index.tsx @@ -13,6 +13,7 @@ import Cog from 'assets/98446.svg'; import { useRouter } from 'expo-router'; import { useColorScheme } from 'nativewind'; import { StatusBar } from 'expo-status-bar'; +import 'react-native-reanimated' export default function Index() { type TaskItem = { @@ -23,8 +24,8 @@ export default function Index() { const [task, setTask] = useState<string>(''); const [taskEntries, setTaskEntries] = useState<TaskItem[]>([]); const [emptyError, setEmptyError] = useState(false); - const router = useRouter(); const {colorScheme, toggleColorScheme} = useColorScheme(); + const router = useRouter(); const handleAddTask = () => { const trimmed = task.trim(); @@ -85,23 +86,23 @@ export default function Index() { > <View className="flex-row items-end justify-between"> <View className="flex-1"> - {emptyError && ( - <Text className="text-center text-red-500 mt-1 ml-3 text-sm">Cannot add empty task!</Text> - )} - <TextInput - className="py-3 px-4 dark:text-white bg-gray-200 dark:bg-neutral-600 rounded-full w-[98%]" - placeholderTextColor={colorScheme=="dark" ? "white" : "black"} - placeholder="Create a Task" - value={task} - onChangeText={text => setTask(text)} - /> - </View> - - <TouchableOpacity onPress={handleAddTask}> - <View className="w-14 h-14 bg-gray-200 dark:bg-neutral-600 rounded-full justify-center items-center"> - <Text className="text-3xl text-sky-500">+</Text> + {emptyError && ( + <Text className="text-center text-red-500 mt-1 ml-3 text-sm">Cannot add empty task!</Text> + )} + <TextInput + className="py-3 px-4 dark:text-white bg-gray-200 dark:bg-neutral-600 rounded-full w-[98%]" + placeholderTextColor={colorScheme=="dark" ? "white" : "black"} + placeholder="Create a Task" + value={task} + onChangeText={text => setTask(text)} + /> </View> - </TouchableOpacity> + + <TouchableOpacity onPress={handleAddTask}> + <View className="w-14 h-14 bg-gray-200 dark:bg-neutral-600 rounded-full justify-center items-center"> + <Text className="text-3xl text-sky-500">+</Text> + </View> + </TouchableOpacity> </View> </KeyboardAvoidingView> </View> diff --git a/app/settings.tsx b/app/settings.tsx index 0799f18f9224d286c63e01d4b49eb040108b6c23..b8890e6a271f55e880c901d2da473e2ccd65cbfa 100644 --- a/app/settings.tsx +++ b/app/settings.tsx @@ -2,6 +2,7 @@ import { TouchableOpacity, Switch, StyleSheet, Text, View } from "react-native"; import { useColorScheme } from 'nativewind'; import { StatusBar } from "expo-status-bar"; import { useRouter } from "expo-router"; +import MarkAnimation from "components/CheckMark" export default function Settings() { const {colorScheme, toggleColorScheme} = useColorScheme(); @@ -13,14 +14,14 @@ export default function Settings() { <Text className="text-3xl text-sky-500">{'<'}</Text> </TouchableOpacity> <StatusBar style={colorScheme=='dark' ? 'light' : 'dark'} /> - <View className="px-5"> - <Text className="text-3xl font-bold">Theme</Text> - <View className="flex-row justify-center items-center"> - <Text className="text-xl dark:text-white">Toggle Theme</Text> - <Switch value={colorScheme=='dark'} onChange={toggleColorScheme} /> + <View className="px-5"> + <Text className="text-3xl font-bold">Theme</Text> + <View className="flex-row justify-center items-center"> + <Text className="text-xl dark:text-white">Toggle Theme</Text> + <Switch value={colorScheme=='dark'} onChange={toggleColorScheme} /> + </View> </View> </View> - </View> ) } diff --git a/babel.config.js b/babel.config.js index d7e68f172040c58cf2a808d243f93526da2083c7..1c5d01c3251312e61a45f8f83606d4d012c1948a 100644 --- a/babel.config.js +++ b/babel.config.js @@ -1,6 +1,6 @@ module.exports = function (api) { api.cache(true); - const plugins = []; + const plugins = ['react-native-reanimated/plugin']; return { presets: [['babel-preset-expo', { jsxImportSource: 'nativewind' }], 'nativewind/babel'], diff --git a/components/CheckMark.tsx b/components/CheckMark.tsx new file mode 100644 index 0000000000000000000000000000000000000000..92a24a23241b7d5fdedda5dfabd7313a34603075 --- /dev/null +++ b/components/CheckMark.tsx @@ -0,0 +1,52 @@ +import { useEffect, useRef } from "react"; +import Svg, { Path } from "react-native-svg"; +import { Animated, Easing} from "react-native"; + +type Props = { + play: boolean; +}; + +const AnimatedPath = Animated.createAnimatedComponent(Path); + +const MarkAnimation = ({ play }: Props) => { + const animation = useRef(new Animated.Value(0)).current; + + useEffect(() => { + if (play) { + animation.setValue(0); + Animated.timing(animation, { + toValue: 1, + duration: 600, + useNativeDriver: true, + easing: Easing.out(Easing.ease), + }).start(); + } + }, [play]); + + const strokeDashoffset = animation.interpolate({ + inputRange: [0, 1], + outputRange: [28, 0], + }); + + return ( + <Svg + viewBox="0 0 24 24" + fill="none" + stroke="green" + strokeWidth={2} + strokeLinecap="round" + strokeLinejoin="round" + width={32} + height={32} + > + <AnimatedPath + d="M4.5 12.75l6 6 9-13.5" + strokeDasharray="28" + strokeDashoffset={strokeDashoffset} + transform="translate(3, -3)" + /> + </Svg> + ); +}; + +export default MarkAnimation; diff --git a/components/Task.tsx b/components/Task.tsx index 2125e30ade88a16d242ea87174915397360269f5..14989ceb9148853132be99391166216a1f7943c2 100644 --- a/components/Task.tsx +++ b/components/Task.tsx @@ -1,5 +1,6 @@ -import React from "react"; import { View, Text, TouchableOpacity } from "react-native"; +import MarkAnimation from "./CheckMark"; +import { useState, useEffect } from "react"; type TaskBox = { text: string; @@ -7,13 +8,20 @@ type TaskBox = { }; const Task = ({ text, opacity = 1 }: TaskBox) => { + const [isGreyedOut, setIsGreyedOut] = useState(false) + + useEffect(() => { + setIsGreyedOut(opacity === 0.4); + }, [opacity]); return ( <View className="bg-gray-200 dark:bg-neutral-600 p-4 rounded-xl flex-row items-center justify-between mb-5" style={{ opacity }}> <View className="flex-row items-center flex-wrap"> <TouchableOpacity className="w-6 h-6 bg-sky-400 opacity-40 mr-4 rounded-sm" /> <Text className="dark:text-white max-w-[80%]">{text}</Text> </View> - <View className="w-4 h-4 border-2 border-blue-500 rounded-sm" /> + <View className="w-6 h-6 border-2 border-sky-400 rounded-sm items-center justify-center"> + {isGreyedOut && <MarkAnimation play={true} />} + </View> </View> ); };