Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions __mocks__/DataStore.mock.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,9 @@ export const DataStore = {
]
} }], */
// async referencedBlocks() { return null },
updateCache(note, shouldUpdateTags = false) {
return note
},
// async saveData() { return null },
async saveJSON(object, filename) {
__json = object
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,13 +152,60 @@ describe(`${PLUGIN_NAME}`, () => {
*/
describe('handleArrowDatesAction()' /* function */, () => {
test("should change the date to the user's choice of >date", async () => {
const before = { content: '>2022-12-31 task content' }
const note = new Note({ filename: 'folder/note.md', type: 'Notes' })
const before = { content: '>2022-12-31 task content', type: 'open', note }
const userChoice = '>2023-01-01'

const result = await handleArrowDatesAction(before, userChoice)
expect(result).toEqual(CONTINUE)
expect(before.content).toEqual('task content >2023-01-01')
})

test('should leave the type alone for an item in a regular note (lite method)', async () => {
const note = new Note({ filename: 'folder/note.md', type: 'Notes' })
const before = { content: '>2022-12-31 task content', type: 'open', note }

await handleArrowDatesAction(before, '>2023-01-01')
expect(before.type).toEqual('open')
})

test('should leave the type alone in a calendar note when useLiteScheduleMethod is set', async () => {
DataStore.settings = { ...DataStore.settings, useLiteScheduleMethod: true }
const note = new Note({ filename: '20221231.md', type: 'Calendar' })
const before = { content: '>2022-12-31 task content', type: 'open', note }

await handleArrowDatesAction(before, '>2023-01-01')
expect(before.content).toEqual('task content >2023-01-01')
expect(before.type).toEqual('open')
DataStore.settings = { ...DataStore.settings, useLiteScheduleMethod: false }
})

test("should mark a calendar note item scheduled and add a back-linked copy in the destination note (NotePlan's full method)", async () => {
const originNote = new Note({ filename: '20221231.md', type: 'Calendar' })
const destNote = new Note({ filename: '20230101.md', type: 'Calendar' })
destNote.insertParagraph = jest.fn()
DataStore.calendarNoteByDateString = jest.fn().mockReturnValue(destNote)
const before = { content: '>2022-12-31 task content', type: 'open', note: originNote }

const result = await handleArrowDatesAction(before, '>2023-01-01')
expect(result).toEqual(CONTINUE)
// original is marked [>] and points at the new date
expect(before.content).toEqual('task content >2023-01-01')
expect(before.type).toEqual('scheduled')
// and a copy back-linking to the original date lands in the destination note
expect(destNote.insertParagraph).toHaveBeenCalledWith('task content <2022-12-31', expect.any(Number), 'open')
})

test('should use checklistScheduled for a checklist item taking the full method', async () => {
const originNote = new Note({ filename: '20221231.md', type: 'Calendar' })
const destNote = new Note({ filename: '20230101.md', type: 'Calendar' })
destNote.insertParagraph = jest.fn()
DataStore.calendarNoteByDateString = jest.fn().mockReturnValue(destNote)
const before = { content: '>2022-12-31 task content', type: 'checklist', note: originNote }

await handleArrowDatesAction(before, '>2023-01-01')
expect(before.type).toEqual('checklistScheduled')
})
})
})
})
5 changes: 4 additions & 1 deletion dwertheimer.TaskAutomations/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@

NOTE: A COUPLE OF RELEASES AFTER 3.0.0 DELETE THE SETTINGS THAT PERTAIN TO TASKS

## [3.1.1] (upcoming)
## [3.2.0] (upcoming)

- Fix (#741): rescheduling an overdue item to a `>date` now uses NotePlan's normal scheduling method for items in calendar notes: the original is marked `[>]` (`scheduled`/`checklistScheduled`) **and** a copy back-linking to the original date (`<origDate`) is added to the destination calendar note. Previously only the `>date` was rewritten, so the item never showed the `[>]` marker. Note: marking the original `[>]` *without* adding the copy would make the item vanish from the new date, so the two always happen together.
- New setting "Use simplified (re)scheduling method?" — if set, rescheduling just updates the `>date` in place (the previous behaviour) with no `[>]` marker and no copy. Items in regular (non-calendar) notes always use this simplified method, matching NotePlan's "Schedule by linking in regular notes" preference.
- New settings "Section heading to add rescheduled tasks under" and "Heading level for new headings", controlling where the copy is placed in the destination note. These mirror the equivalent settings in the Dashboard plugin.
- Fix: "This week" and "Next week" in the follow-up date picker now respect NotePlan's "Start Week On" setting. Previously, when the week started on Monday (or any day other than Sunday), "this week" pointed to the previous week and "next week" to the current week (fix in shared `helpers/NPdateTime.js` getWeekOptions).

## [3.0.0] @dwertheimer 2024-03-26
Expand Down
28 changes: 26 additions & 2 deletions dwertheimer.TaskAutomations/plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
"plugin.name": "✅ Overdue Task Processing",
"plugin.description": "Automations for handling Tasks:\n- Overdue/Forgotten task scanning\n- Task sorting within a note\n- Copying #tags/@mentions from one task to another\n- Mark all tasks in note open/completed\n- Automatically opening URLs of task lines",
"plugin.author": "@dwertheimer",
"plugin.version": "3.1.1",
"plugin.lastUpdateInfo": "Fix: 'This week' and 'Next week' in the follow-up date picker now respect NotePlan's 'Start Week On' setting. Previously, when the week started on Monday (or any day other than Sunday), 'this week' pointed to the previous week and 'next week' to the current week.",
"plugin.version": "3.2.0",
"plugin.lastUpdateInfo": "Rescheduling an overdue item in a calendar note now uses NotePlan's normal scheduling method: the original is marked '[>]' and a back-linked copy is added to the destination note. Set 'Use simplified (re)scheduling method?' to keep the previous in-place behaviour. Also: 'This week' and 'Next week' in the follow-up date picker now respect NotePlan's 'Start Week On' setting.",
"DELETE_ME": "DON'T DELETE commandMigrationMessage or offerToDownloadPlugin until they are documented somewhere",
"commandMigrationMessage": "NOTE: Task Sorting commands have been moved from the Task Automations plugin to the Task Sorting plugin. This plugin has been renamed to 'Overdue Task Processing'.",
"offerToDownloadPlugin": {
Expand Down Expand Up @@ -428,6 +428,30 @@
"type": "heading",
"title": "Overdue Task Scan Settings"
},
{
"key": "useLiteScheduleMethod",
"title": "Use simplified (re)scheduling method?",
"description": "When you reschedule a task to a >date during a review, if set then the item simply has its '>date' updated in the note it is in. It does not show with the special 🕓 [>] task icon, and a copy isn't added into the date it's being scheduled to. If not set, NotePlan's normal method is used: the original is marked '[>] task >newDate' and a copy '[ ] task <origDate' is added to the destination calendar note. Note: tasks in regular (non-calendar) notes always use the simplified method.",
"type": "bool",
"default": false,
"required": true
},
{
"key": "newTaskSectionHeading",
"title": "Section heading to add rescheduled tasks under",
"description": "When NotePlan's normal (re)scheduling method adds a copy of a task into a calendar note, this sets the Section heading to add it under. (Don't include leading #s.) Leave blank to add it at the end of the note. Use '<<top of note>>' or '<<bottom of note>>' to always add at the top/bottom, or '<<carry forward>>' to maintain the current hierarchy of headings in the new note.",
"type": "string",
"default": "",
"required": false
},
{
"key": "newTaskSectionHeadingLevel",
"title": "Heading level for new headings",
"description": "Heading level (1-5) to use when adding new headings in notes. Note: you can also set this to 0, which means add the task under the heading, but only if it already exists.",
"type": "number",
"default": 2,
"required": true
},
{
"key": "numLastUsedChoices",
"title": "Show last N frequently used rescheduling choices at top",
Expand Down
23 changes: 21 additions & 2 deletions dwertheimer.TaskAutomations/src/NPTaskScanAndProcess.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import moment from 'moment/min/moment-with-locales'
import pluginJson from '../plugin.json'
import { moveParagraphToNote } from '@helpers/NPMoveItems'
import { getOverdueParagraphs } from '@helpers/NPParagraph'
import { scheduleItem, scheduleItemLiteMethod } from '@helpers/NPScheduleItems'
import { getNPWeekData, getWeekOptions } from '@helpers/NPdateTime'
import { filterNotesAgainstExcludeFolders, noteType } from '@helpers/note'
import { getReferencedParagraphs, getTodaysReferences } from '@helpers/NPnote'
Expand Down Expand Up @@ -320,12 +321,30 @@ export async function handleOpenTaskAction(origPara: TParagraph): Promise<number
// change the date to the user's choice of >date
export async function handleArrowDatesAction(origPara: TParagraph, userChoice: string, optionChosen?: CommandBarChoice): Promise<number> {
const cmdPressed = optionChosen ? optionChosen.keyModifiers?.length && optionChosen.keyModifiers.includes('cmd') : false
origPara.content = replaceArrowDatesInString(origPara.content, cmdPressed ? '' : userChoice)
updateParagraph(origPara) // Note: after origPara is updated, the pointer is no longer good in Obj-C
if (cmdPressed) {
// CMD means "move this task to that note", so strip the >date here and let processCmdKey() do the move
origPara.content = replaceArrowDatesInString(origPara.content, '')
updateParagraph(origPara) // Note: after origPara is updated, the pointer is no longer good in Obj-C
logDebug(pluginJson, `handleArrowDatesAction: keyModifiers: ${optionChosen ? optionChosen.keyModifiers.toString() : ''}`)
const updatedPara = origPara.note?.paragraphs[origPara.lineIndex] || origPara // get the updated paragraph
await processCmdKey(updatedPara, userChoice)
return CONTINUE
}

// Two ways to (re)schedule, matching jgclark.Dashboard::doRescheduleItem():
// - 'lite': just update the >date in place, leaving the type as open/checklist so the item still shows on the new date.
// - NotePlan's full method: mark the original 'scheduled'/'checklistScheduled' ([>]) *and* add a copy in the destination
// calendar note that back-links to the original date ('<origDate'). Without that copy the item would simply disappear.
// Note: the full method only makes sense for items in calendar notes; items in regular notes are always done 'lite'
// (which is also what NotePlan's "Schedule by linking in regular notes" preference does).
const { useLiteScheduleMethod, newTaskSectionHeading, newTaskSectionHeadingLevel } = DataStore.settings
const dateStrToAdd = userChoice.replace(/^>/, '')
const useLite = origPara.note?.type === 'Notes' || useLiteScheduleMethod
logDebug(pluginJson, `handleArrowDatesAction: scheduling to '${dateStrToAdd}' using ${useLite ? 'lite' : 'NP full'} method`)
if (useLite) {
scheduleItemLiteMethod(origPara, dateStrToAdd)
} else {
scheduleItem(origPara, dateStrToAdd, newTaskSectionHeading ?? '', Number(newTaskSectionHeadingLevel ?? 2))
}
return CONTINUE
}
Expand Down
Loading