diff --git a/src/bases/basesFilterDefaults.ts b/src/bases/basesFilterDefaults.ts index 7b127018..11c3f9f7 100644 --- a/src/bases/basesFilterDefaults.ts +++ b/src/bases/basesFilterDefaults.ts @@ -116,6 +116,23 @@ function applyFilterRuleDefault( return; } + // Generated relationship views (e.g. the default Subtasks tab) normalize each + // link before comparing, producing a rule such as: + // file.hasLink(this.file) && list(note.projects).map(...).contains(this.file.asLink()) + // The generic matcher below cannot reduce that prefix to a field name, so detect + // the normalized list/map form explicitly and recover the wrapped property. + const mappedListContainsCurrentFileMatch = trimmedRule.match( + /(?:^|&&\s*)(list\((?:note\.|task\.)?[\w-]+\))\.map\([\s\S]*\)\.contains\(this\.file\.asLink\(\)\)$/ + ); + if (mappedListContainsCurrentFileMatch) { + const property = normalizeFilterProperty(mappedListContainsCurrentFileMatch[1], options); + const currentFileLink = resolveCurrentFileLink(options.currentFileLink); + if (property && currentFileLink) { + addFrontmatterDefault(defaults, property, currentFileLink, options.fieldMapper); + } + return; + } + const currentFileContainsMatch = trimmedRule.match( /^(.+?)\.contains\(this\.file\.asLink\(\)\)$/ ); diff --git a/tests/unit/bases/basesFilterDefaults.test.ts b/tests/unit/bases/basesFilterDefaults.test.ts index d4833dc6..6ede35a5 100644 --- a/tests/unit/bases/basesFilterDefaults.test.ts +++ b/tests/unit/bases/basesFilterDefaults.test.ts @@ -124,4 +124,51 @@ describe("Bases filter defaults", () => { expect(defaults).toEqual({}); }); + + it("extracts the project default from the generated normalized relationship filter", () => { + // This is the filter the default Subtasks relationship view generates. + const generatedSubtasksFilter = + 'file.hasLink(this.file) && list(note.projects).map(file(value.replace(/^\\[[^\\]]+\\]\\((.*)\\)$/, "$1").replace(/%20/g, " ")).asLink()).contains(this.file.asLink())'; + + const defaults = extractBasesFilterDefaults({ + config: { + query: { + filters: { + conjunction: "and", + filters: [ + { rule: { text: 'file.hasTag("task")' } }, + { rule: { text: generatedSubtasksFilter } }, + ], + }, + }, + }, + fieldMapper: createFieldMapper(), + taskTag: "task", + currentFileLink: "[[Current]]", + }); + + expect(defaults).toEqual({ + projects: ["[[Current]]"], + }); + }); + + it("honors field mapping for the generated normalized relationship filter", () => { + const generatedSubtasksFilter = + 'file.hasLink(this.file) && list(note.projectLinks).map(file(value.replace(/^\\[[^\\]]+\\]\\((.*)\\)$/, "$1").replace(/%20/g, " ")).asLink()).contains(this.file.asLink())'; + + const defaults = extractBasesFilterDefaults({ + config: { + filters: { + rule: { text: generatedSubtasksFilter }, + }, + }, + fieldMapper: createFieldMapper({ projects: "projectLinks" }), + taskTag: "task", + currentFileLink: "[[Current]]", + }); + + expect(defaults).toEqual({ + projectLinks: ["[[Current]]"], + }); + }); });