Works on Dataview task objects (for example dervied from nextActions.js, and toggles status, tags and scheduled dates. Preserves block IDs at the end.
Toggles tasks done using the executeToggleTaskDoneCommand API of the Tasks plugin for Obsidian.
Works on Dataview task objects (for example dervied from nextActions.js, and toggles status, tags and scheduled dates. Preserves block IDs at the end.
Toggles tasks done using the executeToggleTaskDoneCommand API of the Tasks plugin for Obsidian.
/** | |
* Toggles a task in a file based on the given Dataview task action and the given status or scheduled date. | |
* | |
* @param {Object} action - The Dataview task object containing the task details. | |
* @param {string} status - The status to toggle the task to. | |
* @param {string} [date=null] - The date to schedule the task, if applicable. | |
* @return {Promise<void>} A promise that resolves when the task is toggled. | |
*/ | |
async function processFile(file, action, toggledTask) { | |
const cachedData = await app.vault.cachedRead(file); | |
return app.vault.process(file, (data) => { | |
if (cachedData !== data) { | |
data = cachedData; | |
} | |
let lines = data.split("\n"); | |
lines[action.line] = toggledTask; | |
data = lines.join("\n"); | |
return data; | |
}); | |
} | |
function taskStale(action, lines) { | |
return lines[action.line] !== action.text; | |
} | |
async function toggleTask(action, status, date = null, newTag = null) { | |
const dv = app.plugins.plugins["dataview"].api; | |
const { DateTime} = dv.luxon; | |
const file = app.vault.getFileByPath(action.path); | |
const data = await app.vault.cachedRead(file); | |
const lines = data.split("\n"); | |
const tasksAPI = app.plugins.plugins["obsidian-tasks-plugin"].apiV1; | |
let taskText = ""; | |
if (taskStale(action, data)) { | |
taskText = lines[action.line]; | |
} else { | |
taskText = `- [ ] ${action.text}`; | |
} | |
// Regular expressions for task checkboxes, tags and scheduled dates | |
const checkboxRegex = /\-\s{1}\[.\]/g; | |
const todoTagRegex = /(\#todo\/\S+)/m; | |
const scheduledDateRegex = /⏳\s{1}(\d{4}-\d{2}-\d{2})/gu; | |
const path = action.path; | |
let toggledTask = ""; | |
switch(status) { | |
case "done": | |
toggledTask = tasksAPI.executeToggleTaskDoneCommand(taskText, path); | |
break; | |
case "dropped": | |
toggledTask = taskText.replace(checkboxRegex, "- [-]").replace(todoTagRegex, ""); | |
break; | |
case "half-done": | |
toggledTask = taskText.replace(checkboxRegex, "- [/]"); | |
break; | |
case "schedule": | |
const scheduledDate = date; | |
// I had to add .c after scheduled for luxon to be able to parse the date. | |
const scheduled = action.scheduled ? true : false; | |
if (scheduled) { | |
const oldScheduledDate = DateTime.fromObject(action.scheduled.c).toFormat("yyyy-MM-dd"); | |
if (scheduledDate === oldScheduledDate) { | |
new Notice("Task already scheduled today"); | |
return; | |
} | |
} | |
const regexMatch = taskText.match(scheduledDateRegex); | |
const caretIndex = taskText.indexOf("^"); | |
if (regexMatch) { | |
toggledTask = taskText.replace(scheduledDateRegex, (match, p1) => { | |
return match.replace(p1, scheduledDate); | |
}); | |
} else if (caretIndex !== -1) { | |
const beforeCaret = taskText.slice(0, caretIndex); | |
const afterCaret = taskText.slice(caretIndex); | |
toggledTask = `${beforeCaret}⏳ ${scheduledDate} ${afterCaret}`; | |
} else { | |
toggledTask = taskText + `⏳ ${scheduledDate}`; | |
} | |
break; | |
case "re-tag": | |
if (!newTag.startsWith("#")) { | |
newTag = `#${newTag}`; | |
} | |
toggledTask = taskText.replace(todoTagRegex, newTag); | |
break; | |
default: | |
new Notice("Status not recognised"); | |
return; | |
} | |
await processFile(file, action, toggledTask); | |
} | |
module.exports = toggleTask; |