I was inspired by Rosemary’s Complete and Await Reply sample action but find that I am too excited to click the tasks complete button and forget to run her action. Instead, I wanted to create a plugin that would navigate me to the parent of the most recently completed task where I can then manually create the follow-up task. I ran into a couple challenges that maybe other have found better ways to overcome.
Retrieve tasks of perspectives
I have a perspective that shows completed tasks. The most recently completed task is at the top of this list so I tried to retrieve it programatically. With the exception of the inbox, I don’t see a way to retrieve the list of tasks in a perspective without first navigating the window to the perspective.
Modifying the UI unnecessary is distasteful so I instead did a filter() over all tasks in the database. I assume that the query engine used in the perspective is more efficient than JavaScript filter() so it would have been nice to expose it to the automation.
Retrieving parent tasks
Navigating to a task with the omnifocus:///task/ conveniently highlights the task in the Project view. Since some of my projects are large and use task groups, I wanted to highlight the remaining parent of the completed task. I could not figure out a way to directly retrieve the parent of a task and had to resort to an inefficient recursive search on the project. Is there a better way to find a parent task?
URL to completed inbox actions
The OmniFocus:///task/ link for a completed task in the inbox appears to not update the UI.
Completed Plugin
The resulting solution I found is below. Do you have a better way to do this?
/*{
"author": "Mark Patterson",
"targets": ["omnifocus"],
"type": "action",
"identifier": "local.patterson.mark.omnifocus.last_completed",
"version": "0.1",
"description": "A plug-in that opens the project of the last completed action",
"label": "View project of last completed action",
"mediumLabel": "View project of last completed action",
"paletteLabel": "View project of last completed action",
}*/
(() => {
var action = new PlugIn.Action(function(selection) {
// Find the most recently completed task
var last_task = flattenedTasks.reduce((a,b)=>a.completionDate > b.completionDate ? a : b) ;
if (!last_task)
return ;
else if (last_task.project && last_task.project.id.primaryKey == last_task.id.primaryKey) {
// Project root task
var url = "omnifocus:///task/" + last_task.id.primaryKey ;
URL.fromString(url).call(reply=>{}) ;
return ;
}
// Find all parents of the task with a recursive search
function find_parents_recursive(objs) {
for (var i = 0 ; i < objs.length ; i++) {
if (objs[i] == last_task) {
return [objs[i]] ;
} else if (objs[i].hasChildren) {
var p = find_parents_recursive(objs[i].children) ;
if (p) {
p.push(objs[i]) ;
return p ;
}
}
}
return null ;
}
var root ;
if (!last_task.containingProject) // inInbox doesn't work for task groups
root = inbox ;
else
root = [last_task.containingProject] ;
var parents = find_parents_recursive(root) ;
// Choose the lowest remaining parent
var target = last_task ;
for (var i = 0 ; i < parents.length ; i++) {
if (!parents[i].completed) {
target = parents[i] ;
break ;
}
}
// Navigate to the task parent
var url ;
if (target.completed && !target.containingProject) {
// Omnifocus doesn't automatically display for completed tasks in inbox
url = "omnifocus:///inbox" ;
} else {
url = "omnifocus:///task/" + target.id.primaryKey ;
}
URL.fromString(url).call(reply=>{})
});
return action;
})();