Plug-In: Show Latest Active Object

Shows the most recently added or modified active/available OmniFocus folder, project, task, or tag.

Here's the interface presented when the plug-in is launched:

The dialog interface showing the plug-in parameters

Return to: OmniFocus Plug-In Collection

Show Latest Active Object
  

/*{ "type": "action", "targets": ["omnifocus"], "author": "Otto Automator", "identifier": "com.omni-automation.of.show-latest-database-object", "version": "1.0", "description": "Shows the most recently added or modified active OmniFocus folder, project, task, or tag.", "label": "Show the Latest…", "shortLabel": "Show Latest…", "paletteLabel": "Show Latest…", "image": "1.magnifyingglass" }*/ (() => { var preferences = new Preferences() // NO ID = PLUG-IN ID function createUtterance(textToSpeak){ langCode = Speech.Voice.currentLanguageCode voiceObj = Speech.Voice.withLanguage(langCode) utterance = new Speech.Utterance(textToSpeak) utterance.voice = voiceObj utterance.rate = Speech.Utterance.defaultSpeechRate return utterance } const action = new PlugIn.Action(async function(selection, sender){ try { synthesizer = new Speech.Synthesizer() objectTypeIdx = preferences.readNumber("objectTypeIdx") if(!objectTypeIdx){ objectTypeIdx = 0 } console.log("objectTypeIdx", objectTypeIdx) objectType = ["Folder","Project","Task","Tag"] objectTypeMenu = new Form.Field.Option( "objectTypeIdx", "Object Type", [0,1,2,3], objectType, objectTypeIdx ) addedOrModifiedIdx = preferences.readNumber("addedOrModifiedIdx") if(!addedOrModifiedIdx){ addedOrModifiedIdx = 0 } console.log("addedOrModifiedIdx", addedOrModifiedIdx) searchParams = ["Added","Modified"] searchParamMenu = new Form.Field.Option( "addedOrModifiedIdx", "Date Parameter", [0,1], searchParams, addedOrModifiedIdx ) shouldSpeakAlert = preferences.readBoolean("shouldSpeakAlert") if(shouldSpeakAlert === null || shouldSpeakAlert === undefined){ shouldSpeakAlert = true } console.log("shouldSpeakAlert", shouldSpeakAlert) spokenAlertCheckbox = new Form.Field.Checkbox( "shouldSpeakAlert", "Use spoken result alert", shouldSpeakAlert ) inputForm = new Form() inputForm.addField(objectTypeMenu) inputForm.addField(searchParamMenu) inputForm.addField(spokenAlertCheckbox) formPrompt = "Search parameters:" buttonTitle = "Continue" formObject = await inputForm.show(formPrompt, buttonTitle) objectTypeIdx = formObject.values["objectTypeIdx"] preferences.write("objectTypeIdx", objectTypeIdx) objectTypeTerm = objectType[objectTypeIdx] console.log("Object Type:", objectType[objectTypeIdx]) addedOrModifiedIdx = formObject.values["addedOrModifiedIdx"] preferences.write("addedOrModifiedIdx", addedOrModifiedIdx) addedOrModifiedTerm = searchParams[addedOrModifiedIdx] console.log("Date Parameter:", searchParams[addedOrModifiedIdx]) shouldSpeakAlert = formObject.values["shouldSpeakAlert"] preferences.write("shouldSpeakAlert", shouldSpeakAlert) console.log("shouldSpeakAlert:", shouldSpeakAlert) switch (objectTypeIdx) { case 0: // FILTER FOLDERS var filteredObjects = flattenedFolders.filter(folder => { status = folder.status return (status === Folder.Status.Active) }) if(filteredObjects.length === 0){ throw { name: "Missing Object", message: "There are no active folders in the database." } } // SORT FOLDERS if(addedOrModifiedIdx === 0){ filteredObjects.sort((a, b) => b.added - a.added) } else { filteredObjects.sort((a, b) => b.modified - a.modified) } latestObject = filteredObjects[0] latestObject.url.open() break; case 1: // FILTER PROJECTS filteredObjects = flattenedProjects.filter(project => { status = project.status return (status === Project.Status.Active) }) if(filteredObjects.length === 0){ throw { name: "Missing Object", message: "There are no active projects in the database." } } // SORT PROJECTS if(addedOrModifiedIdx === 0){ filteredObjects.sort((a, b) => b.task.added - a.task.added) } else { filteredObjects.sort((a, b) => b.task.modified - a.task.modified) } latestObject = filteredObjects[0] latestObject.url.open() break; case 2: // FILTER TASKS filteredObjects = flattenedTasks.filter(task => { // AVOID ROOT TASKS OF PROJECTS if(task.containingProject !== null){ if(task.id.primaryKey === task.containingProject.id.primaryKey){ return false } } status = task.taskStatus return ( status === Task.Status.Available || status === Task.Status.DueSoon || status === Task.Status.Next ) }) if(filteredObjects.length === 0){ throw { name: "Missing Object", message: "There are no available tasks in the database." } } // SORT TASKS if(addedOrModifiedIdx === 0){ filteredObjects.sort((a, b) => b.added - a.added) } else { filteredObjects.sort((a, b) => b.modified - a.modified) } latestObject = filteredObjects[0] latestObject.url.open() break; default: // FILTER TAGS var filteredObjects = flattenedTags.filter(tag => { status = tag.status return (status === Tag.Status.Active) }) if(filteredObjects.length === 0){ throw { name: "Missing Object", message: "There are no active tags in the database." } } // SORT TAGS if(addedOrModifiedIdx === 0){ filteredObjects.sort((a, b) => b.added - a.added) } else { filteredObjects.sort((a, b) => b.modified - a.modified) } latestObject = filteredObjects[0] latestObject.url.open() break; } // ALERT USER if(shouldSpeakAlert){ utterance = createUtterance(`The most recently ${addedOrModifiedTerm} ${objectTypeTerm}.`) synthesizer.speakUtterance(utterance) } } catch(err){ if(!err.causedByUserCancelling){ utterance = createUtterance(err.message) synthesizer.speakUtterance(utterance) console.error(err.name, err.message) new Alert(err.name, err.message).show() } } }); action.validate = function(selection, sender){ // selection options: tasks, projects, folders, tags, databaseObjects, allObjects return true }; return action; })();