diff --git a/app/src/adapters/input/CreateEffortExoCommand.ts b/app/src/adapters/input/CreateEffortExoCommand.ts new file mode 100644 index 0000000..226226e --- /dev/null +++ b/app/src/adapters/input/CreateEffortExoCommand.ts @@ -0,0 +1,26 @@ +import ExoCommand from "./ExoCommand"; +import ExoContext from "../../../../common/ExoContext"; +import Area from "../../../../core/src/domain/Area"; +import Effort from "../../../../core/src/domain/effort/Effort"; + +export default class CreateEffortExoCommand implements ExoCommand { + name = "Create Effort"; + slug = "create-effort"; + + constructor(private ctx: ExoContext) { + } + + async execute() { + const activeFile = this.ctx.appUtils.getActiveFileOrThrow(); + const activeKo = await this.ctx.kObjectCreator.createFromTFileTyped(activeFile); + if (activeKo instanceof Area) { + let effort = await this.ctx.createEffortUseCase.taskUnderArea(activeKo); + await this.ctx.appUtils.openKObject(effort); + } + + if (activeKo instanceof Effort) { + let effort = await this.ctx.createEffortUseCase.taskUnderEffort(activeKo); + await this.ctx.appUtils.openKObject(effort); + } + } +} diff --git a/app/src/adapters/input/CreateEffortUnderAreaExoCommand.ts b/app/src/adapters/input/CreateEffortUnderAreaExoCommand.ts deleted file mode 100644 index a8ac053..0000000 --- a/app/src/adapters/input/CreateEffortUnderAreaExoCommand.ts +++ /dev/null @@ -1,23 +0,0 @@ -import ExoCommand from "./ExoCommand"; -import ExoContext from "../../../../common/ExoContext"; -import Area from "../../../../core/src/domain/Area"; - -export default class CreateEffortUnderAreaExoCommand implements ExoCommand { - name = "Create Effort Under Area"; - slug = "create-effort-under-area"; - - constructor(private ctx: ExoContext) { - } - - async execute() { - const activeFile = this.ctx.appUtils.getActiveFileOrThrow(); - const activeKo = this.ctx.kObjectCreator.createFromTFileTyped(activeFile); - if (!(activeKo instanceof Area)) { - throw new Error("Active file is not an Area"); - } - - let effort = await this.ctx.createEffortUseCase.taskUnderArea(activeKo); - - await this.ctx.appUtils.openKObject(effort); - } -} diff --git a/app/src/adapters/input/ExoCommands.ts b/app/src/adapters/input/ExoCommands.ts index d25c062..5cf50d7 100644 --- a/app/src/adapters/input/ExoCommands.ts +++ b/app/src/adapters/input/ExoCommands.ts @@ -6,7 +6,7 @@ import GetActiveFileTagsExoCommand from "./GetActiveFileTagsExoCommand"; import GetCurrentKOCExoCommand from "./GetCurrentKOCExoCommand"; import OpenCurrentDailyNoteExoCommand from "./OpenCurrentDailyNoteExoCommand"; import ExoContext from "../../../../common/ExoContext"; -import CreateEffortUnderAreaExoCommand from "./CreateEffortUnderAreaExoCommand"; +import CreateEffortExoCommand from "./CreateEffortExoCommand"; export default class ExoCommands { static all(ctx: ExoContext): ExoCommand[] { @@ -17,7 +17,7 @@ export default class ExoCommands { new GetActiveFileTagsExoCommand(ctx), new GetCurrentKOCExoCommand(ctx), new OpenCurrentDailyNoteExoCommand(ctx, ctx.getCurrentDNUseCase), - new CreateEffortUnderAreaExoCommand(ctx) + new CreateEffortExoCommand(ctx) ]; } diff --git a/app/src/adapters/output/EffortPersistenceAdapter.ts b/app/src/adapters/output/EffortPersistenceAdapter.ts index 5efee5d..77da37d 100644 --- a/app/src/adapters/output/EffortPersistenceAdapter.ts +++ b/app/src/adapters/output/EffortPersistenceAdapter.ts @@ -23,7 +23,7 @@ export default class EffortPersistenceAdapter implements EffortRepository { private serializeData(effort: Effort) { let result = ""; - result += "---\n"; // frontmatter start + result += "---\n"; result += "tags:\n"; result += " - EMS/Effort\n"; result += "uid: " + effort.id + "\n"; @@ -35,9 +35,12 @@ export default class EffortPersistenceAdapter implements EffortRepository { result += "ended: " + effort.ended + "\n"; } if (effort.area) { - result += "area: \"" + this.getLinkToArea(effort.area) + "\"\n"; + result += "area: \'" + this.getLinkToArea(effort.area) + "\'\n"; } - result += "---\n"; // frontmatter end + if (effort.parent) { + result += "e-parent: \'" + this.getLinkToEffort(effort.parent) + "\'\n"; + } + result += "---\n"; result += effort.body; return result; } @@ -45,4 +48,8 @@ export default class EffortPersistenceAdapter implements EffortRepository { private getLinkToArea(area: Area): string { return `[[${area.name}]]`; } + + private getLinkToEffort(parent: Effort): string { + return `[[${parent.title}]]`; + } } diff --git a/app/src/utils/AppUtils.ts b/app/src/utils/AppUtils.ts index 4a8577f..b2b3657 100644 --- a/app/src/utils/AppUtils.ts +++ b/app/src/utils/AppUtils.ts @@ -54,6 +54,16 @@ export default class AppUtils { return this.app.workspace.getActiveFile(); } + getTFileFromStrLink(strLink: string): TFile { + if (strLink.contains("/")) { + const areaFileName = strLink.replace("[[", "").replace("]]", ""); + return this.getFileByPathOrThrow(areaFileName + ".md"); + } else { + const areaFileName = strLink.replace("[[", "").replace("]]", ""); + return this.getFileByName(areaFileName + ".md"); + } + } + getFrontmatterOrNull(file: TFile): FrontMatterCache | null { try { return this.getFrontmatterOrThrow(file) @@ -88,11 +98,19 @@ export default class AppUtils { return []; } - + // TODO rename and add `orThrow` getFileByName(parentFileName: string): TFile { return this.app.vault.getMarkdownFiles().filter(f => f.name == parentFileName)[0]; } + getFileByPathOrThrow(filePath: string): TFile { + let file = this.app.vault.getFileByPath(filePath); + if (!file) { + throw new Error("File not found by path " + filePath); + } + return file; + } + getAllMdFiles() { return this.app.vault.getMarkdownFiles(); } @@ -125,4 +143,8 @@ export default class AppUtils { }); return a[0]; } + + getFileBody(file: TFile) { + return this.app.vault.read(file); + } } diff --git a/app/src/utils/KObjectCreator.ts b/app/src/utils/KObjectCreator.ts index d88fb3f..45cebbf 100644 --- a/app/src/utils/KObjectCreator.ts +++ b/app/src/utils/KObjectCreator.ts @@ -4,6 +4,8 @@ import {KOC} from "../../../core/src/domain/KOC"; import AppUtils from "./AppUtils"; import Area from "../../../core/src/domain/Area"; import {UUID} from "node:crypto"; +import Effort from "../../../core/src/domain/effort/Effort"; +import {EffortStatus} from "../../../core/src/domain/effort/EffortStatus"; export default class KObjectCreator { constructor(private appUtils: AppUtils) { @@ -16,11 +18,13 @@ export default class KObjectCreator { return new KObject(id, koc); } - createFromTFileTyped(file: TFile) { + async createFromTFileTyped(file: TFile) { const koc = this.getFileKoc(file); switch (koc) { case KOC.EMS_AREA: return this.createArea(file); + case KOC.EMS_EFFORT: + return await this.createEffort(file); default: throw new Error("Not implemented createFromTFileTyped") } @@ -34,14 +38,39 @@ export default class KObjectCreator { let parentArea: Area | null = null; const parentStr: string = koProperties["a-parent"]; if (parentStr) { - const parentFileName = parentStr.replace("[[", "").replace("]]", ""); - const parentAreaFile: TFile = this.appUtils.getFileByName(parentFileName + ".md"); - parentArea = this.createArea(parentAreaFile); + const file = this.appUtils.getTFileFromStrLink(parentStr); + parentArea = this.createArea(file); } return new Area(id, file.name.replace(".md", ""), parentArea) } + async createEffort(file: TFile): Promise { + const koProperties = this.appUtils.getFrontmatterOrThrow(file); + + const id: UUID = koProperties["uid"] as UUID; + const status: EffortStatus = koProperties["e-status"] as EffortStatus; + const started: Date | null = koProperties["started"] ? koProperties["started"] as Date : null; + const ended: Date | null = koProperties["ended"] ? koProperties["ended"] as Date : null; + + let area: Area | null = null; + const areaStr: string = koProperties["area"]; + if (areaStr) { + const file = this.appUtils.getTFileFromStrLink(areaStr); + area = this.createArea(file); + } + + let parent: Effort | null = null; + const parentStr: string = koProperties["e-parent"]; + if (parentStr) { + const file = this.appUtils.getTFileFromStrLink(areaStr); + parent = await this.createEffort(file); + } + + const body: string = await this.appUtils.getFileBody(file); + return new Effort(id, file.name.replace(".md", ""), status, started, ended, area, parent, body); + } + getFileKoc(file: TFile): KOC { const tags = this.appUtils.getTagsFromFile(file); diff --git a/core/src/ports/input/CreateEffortUseCase.ts b/core/src/ports/input/CreateEffortUseCase.ts index 3698485..5268c53 100644 --- a/core/src/ports/input/CreateEffortUseCase.ts +++ b/core/src/ports/input/CreateEffortUseCase.ts @@ -3,4 +3,6 @@ import Area from "../../domain/Area"; export default interface CreateEffortUseCase { taskUnderArea(area: Area): Promise; + + taskUnderEffort(parentEffort: Effort): Promise; } diff --git a/core/src/service/CreateEffortService.ts b/core/src/service/CreateEffortService.ts index 4d1a388..a9bc5a3 100644 --- a/core/src/service/CreateEffortService.ts +++ b/core/src/service/CreateEffortService.ts @@ -19,4 +19,14 @@ export default class CreateEffortService implements CreateEffortUseCase { return effort; } + async taskUnderEffort(parentEffort: Effort): Promise { + const title = crypto.randomUUID(); + const id = crypto.randomUUID() as UUID; + const effort = new Effort(id, title, EffortStatus.DRAFT, null, null, null, parentEffort, "Body"); + + await this.effortRepository.save(effort); + + return effort; + } + }