()
| 32 | ] |
| 33 | |
| 34 | export function TasksView(): JSX.Element { |
| 35 | const rawTasks = useStore((s) => s.vaultTasks) |
| 36 | const notes = useStore((s) => s.notes) |
| 37 | const vaultSettings = useStore((s) => s.vaultSettings) |
| 38 | const loading = useStore((s) => s.tasksLoading) |
| 39 | const filter = useStore((s) => s.tasksFilter) |
| 40 | const cursorIndex = useStore((s) => s.taskCursorIndex) |
| 41 | const setFilter = useStore((s) => s.setTasksFilter) |
| 42 | const setCursorIndex = useStore((s) => s.setTaskCursorIndex) |
| 43 | const refreshTasks = useStore((s) => s.refreshTasks) |
| 44 | const openTaskAt = useStore((s) => s.openTaskAt) |
| 45 | const toggleTaskFromList = useStore((s) => s.toggleTaskFromList) |
| 46 | const applyTaskMutation = useStore((s) => s.applyTaskMutation) |
| 47 | const moveTaskToDate = useStore((s) => s.moveTaskToDate) |
| 48 | const addTaskForDate = useStore((s) => s.addTaskForDate) |
| 49 | const closeTasksView = useStore((s) => s.closeTasksView) |
| 50 | const reorderTaskInNote = useStore((s) => s.reorderTaskInNote) |
| 51 | |
| 52 | // Tasks written inside a daily note inherit that note's date as an implicit |
| 53 | // due date (a clean line, no `due:` token) so they appear on the calendar. |
| 54 | // Done at the display layer so it works on desktop + web identically and |
| 55 | // re-derives whenever notes/settings change. Explicit `due:` still wins. |
| 56 | const dueByPath = useMemo( |
| 57 | () => buildDailyNoteDateByPath(notes, vaultSettings), |
| 58 | [notes, vaultSettings] |
| 59 | ) |
| 60 | const tasks = useMemo(() => inferDailyTaskDueDates(rawTasks, dueByPath), [rawTasks, dueByPath]) |
| 61 | const keymapOverrides = useStore((s) => s.keymapOverrides) |
| 62 | const vimMode = useStore((s) => s.vimMode) |
| 63 | const viewMode = useStore((s) => s.tasksViewMode) |
| 64 | const setViewMode = useStore((s) => s.setTasksViewMode) |
| 65 | // Only the Tasks panel in the *active* pane should listen for j/k/etc. |
| 66 | // Splits can show Tasks in multiple panes simultaneously; without this |
| 67 | // gate every keypress would fire once per mounted panel. |
| 68 | const isActivePanel = useStore(isTasksViewActive) |
| 69 | |
| 70 | // Collapse state is local — survives within a session but not across app |
| 71 | // restarts. Done is collapsed by default because it's usually noise. |
| 72 | const [collapsed, setCollapsed] = useState<Record<GroupKey, boolean>>({ |
| 73 | today: false, |
| 74 | upcoming: false, |
| 75 | waiting: false, |
| 76 | done: true |
| 77 | }) |
| 78 | |
| 79 | const filterRef = useRef<HTMLInputElement>(null) |
| 80 | const rootRef = useRef<HTMLDivElement>(null) |
| 81 | const exRef = useRef<HTMLInputElement>(null) |
| 82 | const gPending = useRef(0) |
| 83 | const gTimer = useRef<ReturnType<typeof setTimeout>>() |
| 84 | // After a manual reorder the rows re-sort; keep the cursor on the task that |
| 85 | // moved so repeated Shift+J/K keep nudging the same one. |
| 86 | const followTaskRef = useRef<string | null>(null) |
| 87 | // Vim-style command line. Not backed by CodeMirror (Tasks has no CM |
| 88 | // view) — just a tiny bottom-of-panel input that dispatches a handful |
| 89 | // of ex commands. |
| 90 | const [exOpen, setExOpen] = useState(false) |
| 91 | const [exValue, setExValue] = useState('') |
nothing calls this directly
no test coverage detected