mirror of
https://github.com/callumalpass/tasknotes.git
synced 2026-07-22 12:50:26 +00:00
feat(bases): add dynamic template generation with user-specific filters and properties
- Generate task filters based on user's task identification method (tag or property) - Include property ordering from defaultVisibleProperties setting - Use correct YAML object notation for Bases filters - Support combined filters for project-subtasks view - Maintain backward compatibility with legacy static templates
This commit is contained in:
parent
5630db157c
commit
262a9c5fe6
2 changed files with 272 additions and 7 deletions
21
src/main.ts
21
src/main.ts
|
|
@ -24,7 +24,7 @@ import {
|
|||
import { TaskNotesSettings } from "./types/settings";
|
||||
import { DEFAULT_SETTINGS } from "./settings/defaults";
|
||||
import { TaskNotesSettingTab } from "./settings/TaskNotesSettingTab";
|
||||
import { DEFAULT_BASES_FILES } from "./templates/defaultBasesFiles";
|
||||
import { DEFAULT_BASES_FILES, generateBasesFileTemplate } from "./templates/defaultBasesFiles";
|
||||
import {
|
||||
MINI_CALENDAR_VIEW_TYPE,
|
||||
ADVANCED_CALENDAR_VIEW_TYPE,
|
||||
|
|
@ -1884,14 +1884,21 @@ export default class TaskNotesPlugin extends Plugin {
|
|||
continue;
|
||||
}
|
||||
|
||||
const template = DEFAULT_BASES_FILES[commandId];
|
||||
// Generate template with user settings
|
||||
const template = generateBasesFileTemplate(commandId, this.settings);
|
||||
if (!template) {
|
||||
skipped.push(rawPath);
|
||||
continue;
|
||||
// Fall back to legacy template if generation fails
|
||||
const legacyTemplate = DEFAULT_BASES_FILES[commandId];
|
||||
if (!legacyTemplate) {
|
||||
skipped.push(rawPath);
|
||||
continue;
|
||||
}
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await this.app.vault.create(normalizedPath, legacyTemplate);
|
||||
} else {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await this.app.vault.create(normalizedPath, template);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await this.app.vault.create(normalizedPath, template);
|
||||
created.push(rawPath);
|
||||
}
|
||||
} catch (error) {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,264 @@
|
|||
* These are created in TaskNotes/Views/ directory when the user first uses the commands
|
||||
*/
|
||||
|
||||
import type { TaskNotesSettings } from "../types/settings";
|
||||
|
||||
/**
|
||||
* Generate a task filter expression based on the task identification method
|
||||
* Returns the filter condition string (not the full YAML structure)
|
||||
*/
|
||||
function generateTaskFilterCondition(settings: TaskNotesSettings): string {
|
||||
if (settings.taskIdentificationMethod === "tag") {
|
||||
// Filter by tag using hasTag method
|
||||
const taskTag = settings.taskTag || "task";
|
||||
return `file.hasTag("${taskTag}")`;
|
||||
} else {
|
||||
// Filter by property
|
||||
const propertyName = settings.taskPropertyName;
|
||||
const propertyValue = settings.taskPropertyValue;
|
||||
|
||||
if (!propertyName) {
|
||||
// No property name specified, fall back to tag-based filtering
|
||||
const taskTag = settings.taskTag || "task";
|
||||
return `file.hasTag("${taskTag}")`;
|
||||
}
|
||||
|
||||
if (propertyValue) {
|
||||
// Check property has specific value
|
||||
return `note.${propertyName} == "${propertyValue}"`;
|
||||
} else {
|
||||
// Just check property exists (is not empty)
|
||||
return `note.${propertyName} && note.${propertyName} != "" && note.${propertyName} != null`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format filter condition(s) as YAML object notation
|
||||
*/
|
||||
function formatFilterAsYAML(conditions: string | string[]): string {
|
||||
const conditionArray = Array.isArray(conditions) ? conditions : [conditions];
|
||||
const formattedConditions = conditionArray.map(c => ` - ${c}`).join('\n');
|
||||
return `filters:
|
||||
and:
|
||||
${formattedConditions}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Map internal TaskNotes property names to Bases property names
|
||||
*/
|
||||
function mapPropertyToBasesProperty(property: string, settings: TaskNotesSettings): string {
|
||||
const fieldMapping = settings.fieldMapping;
|
||||
|
||||
switch (property) {
|
||||
case "status":
|
||||
return fieldMapping.status;
|
||||
case "priority":
|
||||
return fieldMapping.priority;
|
||||
case "due":
|
||||
return fieldMapping.due;
|
||||
case "scheduled":
|
||||
return fieldMapping.scheduled;
|
||||
case "contexts":
|
||||
return fieldMapping.contexts;
|
||||
case "projects":
|
||||
return fieldMapping.projects;
|
||||
case "tags":
|
||||
return "file.tags";
|
||||
case "timeEstimate":
|
||||
return fieldMapping.timeEstimate;
|
||||
case "blocked":
|
||||
case "blockedBy":
|
||||
return fieldMapping.blockedBy;
|
||||
case "blocking":
|
||||
// Blocking is a computed property, use blockedBy as the source
|
||||
return fieldMapping.blockedBy;
|
||||
case "recurrence":
|
||||
return fieldMapping.recurrence;
|
||||
case "complete_instances":
|
||||
case "completeInstances":
|
||||
return fieldMapping.completeInstances;
|
||||
case "completedDate":
|
||||
return fieldMapping.completedDate;
|
||||
case "dateCreated":
|
||||
return "file.ctime";
|
||||
case "dateModified":
|
||||
return "file.mtime";
|
||||
case "title":
|
||||
return "file.name";
|
||||
default:
|
||||
return property;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the order array from defaultVisibleProperties
|
||||
*/
|
||||
function generateOrderArray(settings: TaskNotesSettings): string[] {
|
||||
const visibleProperties = settings.defaultVisibleProperties || [
|
||||
"status",
|
||||
"priority",
|
||||
"due",
|
||||
"scheduled",
|
||||
"projects",
|
||||
"contexts",
|
||||
"tags",
|
||||
];
|
||||
|
||||
// Map to Bases property names
|
||||
const basesProperties = visibleProperties.map(prop =>
|
||||
mapPropertyToBasesProperty(prop, settings)
|
||||
);
|
||||
|
||||
// Add essential properties that should always be in the order
|
||||
const essentialProperties = [
|
||||
"file.name", // title
|
||||
mapPropertyToBasesProperty("recurrence", settings),
|
||||
mapPropertyToBasesProperty("complete_instances", settings),
|
||||
];
|
||||
|
||||
// Combine, removing duplicates while preserving order
|
||||
const allProperties: string[] = [];
|
||||
const seen = new Set<string>();
|
||||
|
||||
// Add visible properties first
|
||||
for (const prop of basesProperties) {
|
||||
if (!seen.has(prop)) {
|
||||
allProperties.push(prop);
|
||||
seen.add(prop);
|
||||
}
|
||||
}
|
||||
|
||||
// Add essential properties
|
||||
for (const prop of essentialProperties) {
|
||||
if (!seen.has(prop)) {
|
||||
allProperties.push(prop);
|
||||
seen.add(prop);
|
||||
}
|
||||
}
|
||||
|
||||
return allProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the order array as YAML
|
||||
*/
|
||||
function formatOrderArray(orderArray: string[]): string {
|
||||
return orderArray.map(prop => ` - ${prop}`).join('\n');
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a Bases file template for a specific command with user settings
|
||||
*/
|
||||
export function generateBasesFileTemplate(commandId: string, settings: TaskNotesSettings): string {
|
||||
const taskFilterCondition = generateTaskFilterCondition(settings);
|
||||
const orderArray = generateOrderArray(settings);
|
||||
const orderYaml = formatOrderArray(orderArray);
|
||||
|
||||
switch (commandId) {
|
||||
case 'open-kanban-view':
|
||||
return `# Kanban Board
|
||||
# Automatically filters for tasks based on your settings
|
||||
|
||||
${formatFilterAsYAML(taskFilterCondition)}
|
||||
|
||||
views:
|
||||
- type: tasknotesKanban
|
||||
name: "Kanban Board"
|
||||
order:
|
||||
${orderYaml}
|
||||
options:
|
||||
columnWidth: 280
|
||||
hideEmptyColumns: false
|
||||
`;
|
||||
|
||||
case 'open-tasks-view':
|
||||
return `# All Tasks
|
||||
# Automatically filters for tasks based on your settings
|
||||
|
||||
${formatFilterAsYAML(taskFilterCondition)}
|
||||
|
||||
views:
|
||||
- type: tasknotesTaskList
|
||||
name: "All Tasks"
|
||||
order:
|
||||
${orderYaml}
|
||||
sort:
|
||||
- column: due
|
||||
direction: ASC
|
||||
`;
|
||||
|
||||
case 'open-advanced-calendar-view':
|
||||
return `# Calendar
|
||||
# Automatically filters for tasks based on your settings
|
||||
|
||||
${formatFilterAsYAML(taskFilterCondition)}
|
||||
|
||||
views:
|
||||
- type: tasknotesCalendar
|
||||
name: "Calendar"
|
||||
order:
|
||||
${orderYaml}
|
||||
options:
|
||||
showScheduled: true
|
||||
showDue: true
|
||||
showRecurring: true
|
||||
showTimeEntries: true
|
||||
showTimeblocks: true
|
||||
showPropertyBasedEvents: true
|
||||
calendarView: "timeGridWeek"
|
||||
customDayCount: 3
|
||||
firstDay: 0
|
||||
slotMinTime: "06:00:00"
|
||||
slotMaxTime: "22:00:00"
|
||||
slotDuration: "00:30:00"
|
||||
`;
|
||||
|
||||
case 'open-agenda-view':
|
||||
return `# Agenda
|
||||
# Automatically filters for tasks based on your settings
|
||||
|
||||
${formatFilterAsYAML(taskFilterCondition)}
|
||||
|
||||
views:
|
||||
- type: tasknotesCalendar
|
||||
name: "Agenda"
|
||||
order:
|
||||
${orderYaml}
|
||||
calendarView: "listWeek"
|
||||
startDateProperty: file.ctime
|
||||
listDayCount: 7
|
||||
titleProperty: file.basename
|
||||
`;
|
||||
|
||||
case 'project-subtasks':
|
||||
// This view needs a special filter that combines task filter AND project filter
|
||||
const projectFilter = `note.${settings.fieldMapping.projects}.contains(this.file.asLink())`;
|
||||
return `# Project Subtasks
|
||||
# This view shows all tasks that reference the current file in their projects field
|
||||
# Uses the 'this' keyword to reference the current file dynamically
|
||||
|
||||
${formatFilterAsYAML([taskFilterCondition, projectFilter])}
|
||||
|
||||
views:
|
||||
- type: tasknotesTaskList
|
||||
name: "Subtasks"
|
||||
order:
|
||||
${orderYaml}
|
||||
sort:
|
||||
- column: priority
|
||||
direction: DESC
|
||||
`;
|
||||
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Legacy static templates for backward compatibility
|
||||
* These are used as fallbacks when settings are not available
|
||||
*/
|
||||
export const DEFAULT_BASES_FILES: Record<string, string> = {
|
||||
'open-kanban-view': `views:
|
||||
- type: tasknotesKanban
|
||||
|
|
|
|||
Loading…
Reference in a new issue