import { useRouter } from "expo-router"; import { Pressable, StyleSheet, Text, View } from "react-native"; import Animated, { FadeIn } from "react-native-reanimated"; import { dueMeta, friendlyDate, isDeferred } from "../lib/dates"; import { haptic } from "../lib/haptics"; import { formatEstimate, PRIORITY_COLOR } from "../lib/priority"; import { updateTask } from "../lib/store"; import { C, radius, space, type } from "../lib/theme"; import type { Task, TaskStatus } from "../lib/types"; export default function TaskRow({ task, uncheckTo = "next", showStatus, onLongPress, }: { task: Task; /** Status to revert to when un-checking a done task. */ uncheckTo?: TaskStatus; showStatus?: boolean; /** Drag handle for reorderable lists. */ onLongPress?: () => void; }) { const router = useRouter(); const done = task.status === "done"; // Due date is rendered separately so it can carry a tone color; the reader // sees "due today" in amber, never does date math on an ISO string. const due = task.due_date ? dueMeta(task.due_date) : null; const dueColor = due?.tone === "overdue" ? C.danger : due?.tone === "today" ? C.warn : C.muted; const base = [ showStatus ? task.status : null, task.repeat && "↻", task.context, task.waiting_for && `→ ${task.waiting_for}`, formatEstimate(task.estimate_min), isDeferred(task.defer_date) && `⏸ until ${friendlyDate(task.defer_date!)}`, ] .filter(Boolean) .join(" · "); const meta = base || due; const flag = PRIORITY_COLOR[task.priority]; const toggle = () => { const next = task.status !== "done"; if (next) haptic.success(); else haptic.tap(); updateTask(task.id, { status: next ? "done" : uncheckTo }); }; return ( [s.row, pressed && s.rowPressed]} onPress={() => router.push(`/task/${task.id}`)} onLongPress={onLongPress} delayLongPress={200} > {/* Rounded square rather than a circle — the Things checkbox shape. The border carries the priority colour, so priority is legible at a glance without spending a whole column on a flag. */} {done && ✓} {task.title} {!!meta && ( {base} {!!base && !!due && " · "} {due && {due.label}} )} ); } const s = StyleSheet.create({ row: { flexDirection: "row", alignItems: "center", gap: space.md, paddingVertical: space.md + 2, paddingHorizontal: space.lg, backgroundColor: C.surface, }, rowPressed: { backgroundColor: C.surface2 }, checkbox: { width: 22, height: 22, borderRadius: 7, borderWidth: 1.5, alignItems: "center", justifyContent: "center", }, checkboxDone: { backgroundColor: C.done }, check: { color: C.onAccent, fontSize: 13, lineHeight: 16, fontWeight: "700" }, title: { ...type.row, color: C.text }, titleDone: { color: C.muted, textDecorationLine: "line-through" }, meta: { ...type.meta, color: C.muted, marginTop: 3 }, }); /** Inset hairline sized to line up under the task title, not the checkbox. */ export const TASK_ROW_INSET = space.lg + 22 + space.md; export function TaskSeparator() { return ( ); }