Repeats, estimates, weekly review: completing a repeating task spawns the next occurrence; estimate chips + quick-add tokens (~30m, monthly); review screen with one-tap triage; server gains repeat column and applies its idempotent schema at boot

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-18 00:06:39 -07:00
parent 6ccd417f2a
commit 4ef3f8c63c
22 changed files with 754 additions and 93 deletions

View File

@@ -1,6 +1,7 @@
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";
@@ -21,15 +22,21 @@ export default function TaskRow({
onLongPress?: () => void;
}) {
const router = useRouter();
const meta = [
// 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),
task.due_date && `due ${task.due_date}`,
isDeferred(task.defer_date) && `⏸ until ${friendlyDate(task.defer_date!)}`,
]
.filter(Boolean)
.join(" · ");
const meta = base || due;
const toggle = () => {
const done = task.status !== "done";
@@ -52,7 +59,13 @@ export default function TaskRow({
<Text style={s.title} numberOfLines={2}>
{task.title}
</Text>
{!!meta && <Text style={s.meta}>{meta}</Text>}
{!!meta && (
<Text style={s.meta}>
{base}
{!!base && !!due && " · "}
{due && <Text style={{ color: dueColor }}>{due.label}</Text>}
</Text>
)}
</View>
</Pressable>
</Animated.View>