diff --git a/sample-vault/.obsidian/plugins/leif/main.js b/sample-vault/.obsidian/plugins/leif/main.js index dd596db..05ee5ce 100644 --- a/sample-vault/.obsidian/plugins/leif/main.js +++ b/sample-vault/.obsidian/plugins/leif/main.js @@ -1086,6 +1086,9 @@ var Wall = class { this.notes = notes; } }; +function wallLinkKey(contestId, kind) { + return `${contestId}-${kind}`; +} // src/domain/entities/Contest.ts var Contest = class { @@ -2244,10 +2247,10 @@ async function seedTceSpDemo(dataStore) { contestId: contest.id, wall: { noticeLinks: [ - { id: `${contest.id}-notice`, label: contestSpec.wall.noticeLabel, url: contestSpec.wall.noticeUrl } + { id: wallLinkKey(contest.id, "notice"), label: contestSpec.wall.noticeLabel, url: contestSpec.wall.noticeUrl } ], examLinks: [ - { id: `${contest.id}-exam`, label: contestSpec.wall.examLabel, url: contestSpec.wall.examUrl } + { id: wallLinkKey(contest.id, "exam"), label: contestSpec.wall.examLabel, url: contestSpec.wall.examUrl } ], subjectSnapshots: seededSubjects.map((subject, index) => ({ subjectId: subject.id, @@ -4280,14 +4283,14 @@ var WallTab = class { try { const noticeLink = noticeUrl.value ? [ { - id: `${activeContest.id}-notice`, + id: wallLinkKey(activeContest.id, "notice"), label: noticeLabel.value || "Edital", url: noticeUrl.value } ] : []; const examLink = examUrl.value ? [ { - id: `${activeContest.id}-exam`, + id: wallLinkKey(activeContest.id, "exam"), label: examLabel.value || "Prova", url: examUrl.value } diff --git a/src/domain/entities/Wall.ts b/src/domain/entities/Wall.ts index e1b7a4f..e56bcdc 100644 --- a/src/domain/entities/Wall.ts +++ b/src/domain/entities/Wall.ts @@ -41,4 +41,18 @@ export class Wall { public readonly subjectSnapshots: WallSubjectSnapshot[] = [], public readonly notes?: string ) {} +} + +/** + * The kinds of single-link slots the wall form manages. + */ +export type WallLinkKind = "notice" | "exam"; + +/** + * Builds the stable identifier used for the wall's single notice/exam link. + * The wall form overwrites the link on every save, so a deterministic id + * keeps the array at length 0 or 1 instead of appending duplicates. + */ +export function wallLinkKey(contestId: string, kind: WallLinkKind): string { + return `${contestId}-${kind}`; } \ No newline at end of file diff --git a/src/infrastructure/persistence/Seeder.ts b/src/infrastructure/persistence/Seeder.ts index 9cc26f0..d0647ca 100644 --- a/src/infrastructure/persistence/Seeder.ts +++ b/src/infrastructure/persistence/Seeder.ts @@ -11,6 +11,7 @@ import type { StudyItem } from "@/domain/entities/StudyItem"; import { StudySessionType } from "@/domain/entities/StudySession"; import type { Subject } from "@/domain/entities/Subject"; import type { Topic } from "@/domain/entities/Topic"; +import { wallLinkKey } from "@/domain/entities/Wall"; import { EntityRepositoryFactory } from "@/infrastructure/persistence/EntityRepositoryFactory"; export interface SeededContest { @@ -469,10 +470,10 @@ export async function seedTceSpDemo(dataStore: PluginDataStore): Promise ({ subjectId: subject.id, diff --git a/src/ui/view/components/WallTab.ts b/src/ui/view/components/WallTab.ts index 963ee1a..e2f3498 100644 --- a/src/ui/view/components/WallTab.ts +++ b/src/ui/view/components/WallTab.ts @@ -1,6 +1,7 @@ import type { PluginDataStore } from "@/application/ports/PluginDataStore"; import { UpdateContestWallUseCase } from "@/application/use-cases/UpdateContestWallUseCase"; import type { LeifPluginData } from "@/domain/types/LeifPluginData"; +import { wallLinkKey } from "@/domain/entities/Wall"; import { DomHelpers } from "@/ui/view/shared/DomHelpers"; import { EntityRepositoryFactory } from "@/infrastructure/persistence/EntityRepositoryFactory"; @@ -71,7 +72,7 @@ export class WallTab { const noticeLink = noticeUrl.value ? [ { - id: `${activeContest.id}-notice`, + id: wallLinkKey(activeContest.id, "notice"), label: noticeLabel.value || "Edital", url: noticeUrl.value } @@ -81,7 +82,7 @@ export class WallTab { const examLink = examUrl.value ? [ { - id: `${activeContest.id}-exam`, + id: wallLinkKey(activeContest.id, "exam"), label: examLabel.value || "Prova", url: examUrl.value } diff --git a/tests/domain/EntityClasses.test.ts b/tests/domain/EntityClasses.test.ts index e0ec21b..bb3382b 100644 --- a/tests/domain/EntityClasses.test.ts +++ b/tests/domain/EntityClasses.test.ts @@ -8,7 +8,7 @@ import { Topic } from "@/domain/entities/Topic"; import { StudySession } from "@/domain/entities/StudySession"; import { QuestionNotebook } from "@/domain/entities/QuestionNotebook"; import { ResourceReference } from "@/domain/entities/ResourceReference"; -import { WallLink, WallSubjectSnapshot, Wall } from "@/domain/entities/Wall"; +import { WallLink, WallSubjectSnapshot, Wall, wallLinkKey } from "@/domain/entities/Wall"; import { ValidationError } from "@/domain/errors/DomainErrors"; describe("Entity Classes", () => { @@ -142,6 +142,20 @@ describe("Entity Classes", () => { }); }); + describe("wallLinkKey", () => { + it("builds a stable notice-link id from the contest id", () => { + expect(wallLinkKey("c-1", "notice")).toBe("c-1-notice"); + }); + + it("builds a stable exam-link id from the contest id", () => { + expect(wallLinkKey("c-1", "exam")).toBe("c-1-exam"); + }); + + it("returns the same id for the same contest + kind", () => { + expect(wallLinkKey("c-1", "notice")).toBe(wallLinkKey("c-1", "notice")); + }); + }); + describe("ContestState", () => { it("creates a valid state", () => { const state = new ContestState("c-1", "s-1", "i-1");