Skip to content

Commit

Permalink
Merge pull request #1723 from vanadium23/feature-task-created-date
Browse files Browse the repository at this point in the history
feat: add created date to task

See Discussion #98.
  • Loading branch information
claremacrae committed Mar 8, 2023
2 parents d7593a2 + 8ed52a9 commit 94d4f69
Show file tree
Hide file tree
Showing 14 changed files with 225 additions and 6 deletions.
13 changes: 13 additions & 0 deletions docs/getting-started/dates.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,19 @@ starts before tomorrow
```
````

## ➕ Created

It can happen that you want to log, when task was created.
Obsidian tasks can automatically add created date for tasks created via 'Create or edit task', when settings 'Set created date on every added task' is enabled.

Created dates use a heavy plus emoji.

```markdown
- [ ] take out the trash ➕ 2021-04-09
```

Filtering currently is not implemented for created field.

## Finding mistakes in dates

Tasks does not automatically report any problem tasks that have invalid dates, such as on the 32nd day of a month. These task will silently not be found by date-based searches.
Expand Down
1 change: 1 addition & 0 deletions docs/queries/layout.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ The following elements exist:
- `backlink`
- `urgency`
- `priority`
- `created date`
- `start date`
- `scheduled date`
- `due date`
Expand Down
1 change: 1 addition & 0 deletions docs/quick-reference/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ This table summarizes the filters and other options available inside a `tasks` b
| `done (before, after, on) <date>`<br>`has done date`<br>`no done date`<br>`done date is invalid` | `sort by done` | `group by done` | `hide done date` |
| `status.name (includes, does not include) <string>`<br>`status.name (regex matches, regex does not match) /regex/i` | `sort by status.name` | `group by status.name` | |
| `status.type (is, is not) (TODO, DONE, IN_PROGRESS, CANCELLED, NON_TASK)` | `sort by status.type` | `group by status.type` | |
| | | | `hide created date` |
| `starts (before, after, on) <date>`<br>`has start date`<br>`no start date`<br>`start date is invalid` | `sort by start` | `group by start` | `hide start date` |
| `scheduled (before, after, on) <date>`<br>`has scheduled date`<br>`no scheduled date`<br>`scheduled date is invalid` | `sort by scheduled` | `group by scheduled` | `hide scheduled date` |
| `due (before, after, on) <date>`<br>`has due date`<br>`no due date`<br>`due date is invalid` | `sort by due` | `group by due` | `hide due date` |
Expand Down
10 changes: 10 additions & 0 deletions src/Commands/CreateOrEditTaskParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Priority, Task, TaskRegularExpressions } from '../Task';
import { DateFallback } from '../DateFallback';
import { StatusRegistry } from '../StatusRegistry';
import { TaskLocation } from '../TaskLocation';
import { getSettings } from '../Config/Settings';

/**
* Read any markdown line and treat it as a task, for the purposes of
Expand Down Expand Up @@ -30,12 +31,19 @@ export const taskFromLine = ({ line, path }: { line: string; path: string }): Ta
return task;
}

const { setCreatedDate } = getSettings();
let createdDate: moment.Moment | null = null;
if (setCreatedDate) {
createdDate = window.moment();
}

// If we are not on a line of a task, we take what we have.
// The non-task line can still be a checklist, for example if it is lacking the global filter.
const nonTaskMatch = line.match(TaskRegularExpressions.nonTaskRegex);
if (nonTaskMatch === null) {
// Should never happen; everything in the regex is optional.
console.error('Tasks: Cannot create task on line:', line);

return new Task({
status: Status.TODO,
description: '',
Expand All @@ -44,6 +52,7 @@ export const taskFromLine = ({ line, path }: { line: string; path: string }): Ta
indentation: '',
listMarker: '-',
priority: Priority.None,
createdDate,
startDate: null,
scheduledDate: null,
dueDate: null,
Expand Down Expand Up @@ -79,6 +88,7 @@ export const taskFromLine = ({ line, path }: { line: string; path: string }): Ta
listMarker,
blockLink,
priority: Priority.None,
createdDate,
startDate: null,
scheduledDate: null,
dueDate: null,
Expand Down
2 changes: 2 additions & 0 deletions src/Config/Settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export type HeadingState = {
export interface Settings {
globalFilter: string;
removeGlobalFilter: boolean;
setCreatedDate: boolean;
setDoneDate: boolean;
autoSuggestInEditor: boolean;
autoSuggestMinMatch: number;
Expand All @@ -42,6 +43,7 @@ export interface Settings {
const defaultSettings: Settings = {
globalFilter: '',
removeGlobalFilter: false,
setCreatedDate: false,
setDoneDate: true,
autoSuggestInEditor: true,
autoSuggestMinMatch: 0,
Expand Down
13 changes: 13 additions & 0 deletions src/Config/SettingsTab.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,19 @@ export class SettingsTab extends PluginSettingTab {
containerEl.createEl('h4', { text: 'Date Settings' });
// ---------------------------------------------------------------------------

new Setting(containerEl)
.setName('Set created date on every added task')
.setDesc(
"Enabling this will add a timestamp ➕ YYYY-MM-DD before other date values in a task is created with 'Create or edit task'",
)
.addToggle((toggle) => {
const settings = getSettings();
toggle.setValue(settings.setCreatedDate).onChange(async (value) => {
updateSettings({ setCreatedDate: value });
await this.plugin.saveSettings();
});
});

new Setting(containerEl)
.setName('Set done date on every completed task')
.setDesc('Enabling this will add a timestamp ✅ YYYY-MM-DD at the end when a task is toggled to done')
Expand Down
5 changes: 4 additions & 1 deletion src/Query/Query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export class Query implements IQuery {
/^group by (backlink|done|due|filename|folder|happens|heading|path|priority|recurrence|recurring|root|scheduled|start|status|tags)/;

private readonly hideOptionsRegexp =
/^(hide|show) (task count|backlink|priority|start date|scheduled date|done date|due date|recurrence rule|edit button|urgency)/;
/^(hide|show) (task count|backlink|priority|created date|start date|scheduled date|done date|due date|recurrence rule|edit button|urgency)/;
private readonly shortModeRegexp = /^short/;
private readonly explainQueryRegexp = /^explain/;

Expand Down Expand Up @@ -162,6 +162,9 @@ export class Query implements IQuery {
case 'priority':
this._layoutOptions.hidePriority = hide;
break;
case 'created date':
this._layoutOptions.hideCreatedDate = hide;
break;
case 'start date':
this._layoutOptions.hideStartDate = hide;
break;
Expand Down
29 changes: 28 additions & 1 deletion src/Task.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export const prioritySymbols = {

export const recurrenceSymbol = '🔁';
export const startDateSymbol = '🛫';
export const createdDateSymbol = '➕';
export const scheduledDateSymbol = '⏳';
export const dueDateSymbol = '📅';
export const doneDateSymbol = '✅';
Expand Down Expand Up @@ -92,6 +93,7 @@ export class TaskRegularExpressions {
// removed from the end until none are left.
public static readonly priorityRegex = /([⏫🔼🔽])$/u;
public static readonly startDateRegex = /🛫 *(\d{4}-\d{2}-\d{2})$/u;
public static readonly createdDateRegex = /➕ *(\d{4}-\d{2}-\d{2})$/u;
public static readonly scheduledDateRegex = /[⏳⌛] *(\d{4}-\d{2}-\d{2})$/u;
public static readonly dueDateRegex = /[📅📆🗓] *(\d{4}-\d{2}-\d{2})$/u;
public static readonly doneDateRegex = /✅ *(\d{4}-\d{2}-\d{2})$/u;
Expand Down Expand Up @@ -128,6 +130,7 @@ export class Task {

public readonly priority: Priority;

public readonly createdDate: Moment | null;
public readonly startDate: Moment | null;
public readonly scheduledDate: Moment | null;
public readonly dueDate: Moment | null;
Expand All @@ -154,6 +157,7 @@ export class Task {
indentation,
listMarker,
priority,
createdDate,
startDate,
scheduledDate,
dueDate,
Expand All @@ -170,6 +174,7 @@ export class Task {
indentation: string;
listMarker: string;
priority: Priority;
createdDate: moment.Moment | null;
startDate: moment.Moment | null;
scheduledDate: moment.Moment | null;
dueDate: moment.Moment | null;
Expand All @@ -190,6 +195,7 @@ export class Task {

this.priority = priority;

this.createdDate = createdDate;
this.startDate = startDate;
this.scheduledDate = scheduledDate;
this.dueDate = dueDate;
Expand Down Expand Up @@ -264,6 +270,7 @@ export class Task {
let scheduledDateIsInferred = false;
let dueDate: Moment | null = null;
let doneDate: Moment | null = null;
let createdDate: Moment | null = null;
let recurrenceRule: string = '';
let recurrence: Recurrence | null = null;
let tags: any = [];
Expand Down Expand Up @@ -323,6 +330,13 @@ export class Task {
matched = true;
}

const createdDateMatch = description.match(TaskRegularExpressions.createdDateRegex);
if (createdDateMatch !== null) {
createdDate = window.moment(createdDateMatch[1], TaskRegularExpressions.dateFormat);
description = description.replace(TaskRegularExpressions.createdDateRegex, '').trim();
matched = true;
}

const recurrenceMatch = description.match(TaskRegularExpressions.recurrenceRegex);
if (recurrenceMatch !== null) {
// Save the recurrence rule, but *do not parse it yet*.
Expand Down Expand Up @@ -385,6 +399,7 @@ export class Task {
listMarker,
taskLocation: taskLocation,
priority,
createdDate,
startDate,
scheduledDate,
dueDate,
Expand Down Expand Up @@ -444,6 +459,11 @@ export class Task {
return layout.options.shortMode
? ' ' + startDateSymbol
: ` ${startDateSymbol} ${this.startDate.format(TaskRegularExpressions.dateFormat)}`;
case 'createdDate':
if (!this.createdDate) return '';
return layout.options.shortMode
? ' ' + createdDateSymbol
: ` ${createdDateSymbol} ${this.createdDate.format(TaskRegularExpressions.dateFormat)}`;
case 'scheduledDate':
if (!this.scheduledDate || this.scheduledDateIsInferred) return '';
return layout.options.shortMode
Expand Down Expand Up @@ -522,6 +542,11 @@ export class Task {
const newTasks: Task[] = [];

if (nextOccurrence !== null) {
const { setCreatedDate } = getSettings();
let createdDate: moment.Moment | null = null;
if (setCreatedDate) {
createdDate = window.moment();
}
const nextStatus = StatusRegistry.getInstance().getNextStatusOrCreate(newStatus);
const nextTask = new Task({
...this,
Expand All @@ -530,6 +555,8 @@ export class Task {
// New occurrences cannot have the same block link.
// And random block links don't help.
blockLink: '',
// add new createdDate on reccuring tasks
createdDate,
});
newTasks.push(nextTask);
}
Expand Down Expand Up @@ -679,7 +706,7 @@ export class Task {
}

// Compare Date fields
args = ['startDate', 'scheduledDate', 'dueDate', 'doneDate'];
args = ['createdDate', 'startDate', 'scheduledDate', 'dueDate', 'doneDate'];
for (const el of args) {
const date1 = this[el] as Moment | null;
const date2 = other[el] as Moment | null;
Expand Down
4 changes: 4 additions & 0 deletions src/TaskLayout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export class LayoutOptions {
hideTaskCount: boolean = false;
hideBacklinks: boolean = false;
hidePriority: boolean = false;
hideCreatedDate: boolean = false;
hideStartDate: boolean = false;
hideScheduledDate: boolean = false;
hideDoneDate: boolean = false;
Expand All @@ -20,6 +21,7 @@ export type TaskLayoutComponent =
| 'description'
| 'priority'
| 'recurrenceRule'
| 'createdDate'
| 'startDate'
| 'scheduledDate'
| 'dueDate'
Expand All @@ -36,6 +38,7 @@ export class TaskLayout {
'description',
'priority',
'recurrenceRule',
'createdDate',
'startDate',
'scheduledDate',
'dueDate',
Expand Down Expand Up @@ -81,6 +84,7 @@ export class TaskLayout {
let newComponents = this.layoutComponents;
newComponents = removeIf(newComponents, layoutOptions.hidePriority, 'priority');
newComponents = removeIf(newComponents, layoutOptions.hideRecurrenceRule, 'recurrenceRule');
newComponents = removeIf(newComponents, layoutOptions.hideCreatedDate, 'createdDate');
newComponents = removeIf(newComponents, layoutOptions.hideStartDate, 'startDate');
newComponents = removeIf(newComponents, layoutOptions.hideScheduledDate, 'scheduledDate');
newComponents = removeIf(newComponents, layoutOptions.hideDueDate, 'dueDate');
Expand Down
10 changes: 10 additions & 0 deletions src/TaskLineRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,16 @@ function addTooltip({
recurrenceDiv.setText(`${taskModule.recurrenceSymbol} ${task.recurrence.toText()}`);
}

if (task.createdDate) {
const createdDateDiv = tooltip.createDiv();
createdDateDiv.setText(
toTooltipDate({
signifier: taskModule.createdDateSymbol,
date: task.createdDate,
}),
);
}

if (task.startDate) {
const startDateDiv = tooltip.createDiv();
startDateDiv.setText(
Expand Down
4 changes: 4 additions & 0 deletions tests/Query.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ describe('Query parsing', () => {
'# Comment lines are ignored',
'explain',
'hide backlink',
'hide created date',
'hide done date',
'hide due date',
'hide edit button',
Expand All @@ -231,6 +232,7 @@ describe('Query parsing', () => {
'show priority',
'show recurrence rule',
'show scheduled date',
'show created date',
'show start date',
'show task count',
'show urgency',
Expand Down Expand Up @@ -293,6 +295,7 @@ describe('Query', () => {
tags: [],
originalMarkdown: '',
scheduledDateIsInferred: false,
createdDate: null,
}),
new Task({
status: Status.TODO,
Expand All @@ -310,6 +313,7 @@ describe('Query', () => {
tags: [],
originalMarkdown: '',
scheduledDateIsInferred: false,
createdDate: null,
}),
];
const input = 'path includes ab/c d';
Expand Down
Loading

0 comments on commit 94d4f69

Please sign in to comment.