You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
114 lines
3.4 KiB
114 lines
3.4 KiB
import { VStack } from "@/components/ui/vstack";
|
|
import { Text } from "@/components/ui/text";
|
|
import { View } from "react-native";
|
|
import {
|
|
Checkbox,
|
|
CheckboxIcon,
|
|
CheckboxIndicator,
|
|
CheckboxLabel,
|
|
} from "@/components/ui/checkbox";
|
|
import { useActionState, useEffect, useRef, useState } from "react";
|
|
import { AddIcon, CheckIcon } from "@/components/ui/icon";
|
|
import TodoContainer, { Todo } from "@/components/containers/todo-container";
|
|
import { FormControl } from "@/components/ui/form-control";
|
|
import { Input, InputField, InputIcon } from "@/components/ui/input";
|
|
import { Pressable } from "@/components/ui/pressable";
|
|
import uuid from "react-native-uuid";
|
|
import AsyncStorage from "@react-native-async-storage/async-storage";
|
|
|
|
const STORAGE_KEY = "todos:v1";
|
|
|
|
export default function TodoTab() {
|
|
const [item, setItem] = useState("");
|
|
const [todos, setTodos] = useState<Todo[]>([]);
|
|
|
|
// 초기 로드
|
|
useEffect(() => {
|
|
(async () => {
|
|
try {
|
|
const raw = await AsyncStorage.getItem(STORAGE_KEY);
|
|
if (raw) {
|
|
const parsed = JSON.parse(raw) as Todo[];
|
|
setTodos(parsed);
|
|
}
|
|
} catch (e) {
|
|
console.warn("Failed to load todos: ", e);
|
|
}
|
|
})();
|
|
}, []);
|
|
|
|
// 변경 시 저장 (디바운스 300ms)
|
|
const saveTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
useEffect(() => {
|
|
if (saveTimer.current) clearTimeout(saveTimer.current);
|
|
saveTimer.current = setTimeout(async () => {
|
|
try {
|
|
await AsyncStorage.setItem(STORAGE_KEY, JSON.stringify(todos));
|
|
} catch (e) {
|
|
console.warn("Failed to save todos: ", e);
|
|
}
|
|
}, 300);
|
|
|
|
return () => {
|
|
if (saveTimer.current) clearTimeout(saveTimer.current);
|
|
};
|
|
}, [todos]);
|
|
|
|
const addTodo = (task: string) => {
|
|
const lastTodo = todos[todos?.length - 1];
|
|
if (lastTodo?.task !== "" && task !== "") {
|
|
setTodos([
|
|
...todos,
|
|
{
|
|
id: uuid.v4(),
|
|
task: task,
|
|
completed: false,
|
|
},
|
|
]);
|
|
setItem("");
|
|
}
|
|
};
|
|
|
|
const toggleTodo = (id: string) => {
|
|
const updatedTodos = todos?.map((todo) => {
|
|
if (todo.id === id) {
|
|
todo.completed = !todo.completed;
|
|
}
|
|
return todo;
|
|
});
|
|
setTodos(updatedTodos);
|
|
};
|
|
|
|
const deleteTodo = (id: string) => {
|
|
const updatedTodos = todos.filter((todo) => todo.id !== id);
|
|
setTodos(updatedTodos);
|
|
};
|
|
|
|
return (
|
|
<VStack className="flex-1 bg-secondary-100 md:items-center md:justify-center">
|
|
<VStack className="rounded-md bg-secondary-100 md:h-[500px] md:w-[700px]">
|
|
<FormControl className="my-4">
|
|
<Input variant="underlined" size="sm" className="mx-6 my-2">
|
|
<InputField
|
|
placeholder="다음 할 일은 무엇인가요?"
|
|
value={item}
|
|
onChangeText={(value) => setItem(value)}
|
|
onSubmitEditing={() => addTodo(item)}
|
|
/>
|
|
<Pressable onPress={() => addTodo(item)}>
|
|
<InputIcon as={AddIcon} className="cursor-pointer h-3 w-3" />
|
|
</Pressable>
|
|
</Input>
|
|
</FormControl>
|
|
{todos?.map((todo: Todo, index: number) => (
|
|
<TodoContainer
|
|
key={index}
|
|
todo={todo}
|
|
toggleTodo={toggleTodo}
|
|
deleteTodo={deleteTodo}
|
|
/>
|
|
))}
|
|
</VStack>
|
|
</VStack>
|
|
);
|
|
}
|
|
|