Plug-In: Show Latest Active Object
Shows the most recently added or modified active/available OmniFocus folder, project, task, or tag.
- For Folders, only those with Active status are considered.
- For Projects, only those with Active status are considered.
- For Tasks, only those whose status is Available, Due Soon, or Next, are considered.
- For Tags, only those with Active status are considered.
Here's the interface presented when the plug-in is launched:
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 IDfunction createUtterance(textToSpeak){langCode = Speech.Voice.currentLanguageCodevoiceObj = Speech.Voice.withLanguage(langCode)utterance = new Speech.Utterance(textToSpeak)utterance.voice = voiceObjutterance.rate = Speech.Utterance.defaultSpeechRatereturn 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 FOLDERSvar filteredObjects = flattenedFolders.filter(folder => {status = folder.statusreturn (status === Folder.Status.Active)})if(filteredObjects.length === 0){throw {name: "Missing Object",message: "There are no active folders in the database."}}// SORT FOLDERSif(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 PROJECTSfilteredObjects = flattenedProjects.filter(project => {status = project.statusreturn (status === Project.Status.Active)})if(filteredObjects.length === 0){throw {name: "Missing Object",message: "There are no active projects in the database."}}// SORT PROJECTSif(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 TASKSfilteredObjects = flattenedTasks.filter(task => {// AVOID ROOT TASKS OF PROJECTSif(task.containingProject !== null){if(task.id.primaryKey === task.containingProject.id.primaryKey){return false}}status = task.taskStatusreturn (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 TASKSif(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 TAGSvar filteredObjects = flattenedTags.filter(tag => {status = tag.statusreturn (status === Tag.Status.Active)})if(filteredObjects.length === 0){throw {name: "Missing Object",message: "There are no active tags in the database."}}// SORT TAGSif(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 USERif(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, allObjectsreturn true};return action;})();