Todoist-style: priorities P1-P4, estimates, FAB add form, Today view, natural quick add, drag-and-drop with haptics
All checks were successful
Build & Release APK / build (push) Successful in 1m53s
All checks were successful
Build & Release APK / build (push) Successful in 1m53s
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
80
app/src/components/TaskRow.tsx
Normal file
80
app/src/components/TaskRow.tsx
Normal file
@@ -0,0 +1,80 @@
|
||||
import { useRouter } from "expo-router";
|
||||
import { Pressable, StyleSheet, Text, View } from "react-native";
|
||||
import Animated, { FadeIn } from "react-native-reanimated";
|
||||
import { haptic } from "../lib/haptics";
|
||||
import { formatEstimate, PRIORITY_COLOR } from "../lib/priority";
|
||||
import { updateTask } from "../lib/store";
|
||||
import { C } from "../lib/theme";
|
||||
import type { Task, TaskStatus } from "../lib/types";
|
||||
|
||||
export default function TaskRow({
|
||||
task,
|
||||
uncheckTo = "next",
|
||||
showStatus,
|
||||
}: {
|
||||
task: Task;
|
||||
/** Status to revert to when un-checking a done task. */
|
||||
uncheckTo?: TaskStatus;
|
||||
showStatus?: boolean;
|
||||
}) {
|
||||
const router = useRouter();
|
||||
const meta = [
|
||||
showStatus ? task.status : null,
|
||||
task.context,
|
||||
task.waiting_for && `→ ${task.waiting_for}`,
|
||||
formatEstimate(task.estimate_min),
|
||||
task.due_date && `due ${task.due_date}`,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(" · ");
|
||||
|
||||
const toggle = () => {
|
||||
const done = task.status !== "done";
|
||||
if (done) haptic.success();
|
||||
else haptic.tap();
|
||||
updateTask(task.id, { status: done ? "done" : uncheckTo });
|
||||
};
|
||||
|
||||
return (
|
||||
<Animated.View entering={FadeIn.duration(150)}>
|
||||
<Pressable style={s.row} onPress={() => router.push(`/task/${task.id}`)}>
|
||||
<Pressable
|
||||
style={[s.checkbox, { borderColor: PRIORITY_COLOR[task.priority] }]}
|
||||
hitSlop={10}
|
||||
onPress={toggle}
|
||||
>
|
||||
{task.status === "done" && <View style={s.checkboxFill} />}
|
||||
</Pressable>
|
||||
<View style={{ flex: 1 }}>
|
||||
<Text style={s.title} numberOfLines={2}>
|
||||
{task.title}
|
||||
</Text>
|
||||
{!!meta && <Text style={s.meta}>{meta}</Text>}
|
||||
</View>
|
||||
</Pressable>
|
||||
</Animated.View>
|
||||
);
|
||||
}
|
||||
|
||||
const s = StyleSheet.create({
|
||||
row: {
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
gap: 12,
|
||||
paddingVertical: 14,
|
||||
paddingHorizontal: 16,
|
||||
borderBottomWidth: StyleSheet.hairlineWidth,
|
||||
borderBottomColor: C.border,
|
||||
},
|
||||
checkbox: {
|
||||
width: 22,
|
||||
height: 22,
|
||||
borderRadius: 11,
|
||||
borderWidth: 2,
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
},
|
||||
checkboxFill: { width: 12, height: 12, borderRadius: 6, backgroundColor: C.done },
|
||||
title: { color: C.text, fontSize: 16 },
|
||||
meta: { color: C.muted, fontSize: 13, marginTop: 2 },
|
||||
});
|
||||
Reference in New Issue
Block a user