diff --git a/src/ui/ICSCard.ts b/src/ui/ICSCard.ts index 5e79a3cc3..10528ca32 100644 --- a/src/ui/ICSCard.ts +++ b/src/ui/ICSCard.ts @@ -46,6 +46,30 @@ function renderRelatedNoteIndicator( }); } +function getEventSourceName(icsEvent: ICSEvent, plugin: TaskNotesPlugin): string { + const subscription = plugin.icsSubscriptionService + ?.getSubscriptions() + .find((candidate) => candidate.id === icsEvent.subscriptionId); + if (subscription?.name) { + return subscription.name; + } + + const provider = plugin.calendarProviderRegistry?.findProviderForEvent(icsEvent); + if (provider) { + const { calendarId } = provider.extractEventIds(icsEvent); + const calendar = provider + .getAvailableCalendars() + .find( + (candidate) => + candidate.id === calendarId || + (calendarId === "primary" && candidate.primary === true) + ); + return calendar?.summary || provider.providerName; + } + + return plugin.i18n.translate("ui.icsCard.calendarFallback"); +} + function formatTimeRange(icsEvent: ICSEvent, plugin: TaskNotesPlugin): string { try { if (!icsEvent.start) return ""; @@ -85,12 +109,12 @@ export function createICSEventCard( card.dataset.relatedNoteCount = String(opts.relatedNoteCount); } - // Determine subscription color and name + // Determine subscription color and source name const subscription = plugin.icsSubscriptionService ?.getSubscriptions() .find((s) => s.id === icsEvent.subscriptionId); const color = icsEvent.color || subscription?.color || "var(--color-accent)"; - const sourceName = subscription?.name || plugin.i18n.translate("ui.icsCard.calendarFallback"); + const sourceName = getEventSourceName(icsEvent, plugin); // Main row const mainRow = card.createEl("div", { cls: "task-card__main-row" }); @@ -228,7 +252,7 @@ export function updateICSEventCard( ?.getSubscriptions() .find((s) => s.id === icsEvent.subscriptionId); const color = icsEvent.color || subscription?.color || "var(--color-accent)"; - const sourceName = subscription?.name || plugin.i18n.translate("ui.icsCard.calendarFallback"); + const sourceName = getEventSourceName(icsEvent, plugin); // Update icon color on wrapper to propagate to svg (icons use currentColor) element.style.setProperty("--current-status-color", color); diff --git a/tests/unit/issues/issue-provider-calendar-source-name.test.ts b/tests/unit/issues/issue-provider-calendar-source-name.test.ts new file mode 100644 index 000000000..383c14a09 --- /dev/null +++ b/tests/unit/issues/issue-provider-calendar-source-name.test.ts @@ -0,0 +1,90 @@ +import { describe, expect, it, jest } from "@jest/globals"; +import { createICSEventCard, updateICSEventCard } from "../../../src/ui/ICSCard"; +import type { ICSEvent } from "../../../src/types"; + +function createEvent(overrides: Partial = {}): ICSEvent { + return { + id: "google-primary-event-1", + subscriptionId: "google-primary", + title: "Team sync", + start: "2026-08-03T10:00:00", + end: "2026-08-03T11:00:00", + allDay: false, + ...overrides, + }; +} + +function createPlugin() { + const provider = { + providerName: "Google Calendar", + extractEventIds: (event: ICSEvent) => ({ + calendarId: event.subscriptionId.replace("google-", ""), + eventId: event.id, + }), + getAvailableCalendars: jest.fn(() => [ + { + id: "person@example.com", + summary: "Personal", + primary: true, + }, + ]), + }; + + return { + app: {}, + i18n: { + translate: (key: string) => (key === "ui.icsCard.calendarFallback" ? "Calendar" : key), + }, + settings: { + calendarViewSettings: { + timeFormat: "24", + }, + }, + icsSubscriptionService: { + getSubscriptions: () => [], + }, + calendarProviderRegistry: { + findProviderForEvent: jest.fn(() => provider), + }, + }; +} + +describe("provider calendar source names on ICS cards", () => { + it("shows a provider calendar name for primary-alias Google events", () => { + const card = createICSEventCard(createEvent(), createPlugin() as any); + + expect(card.querySelector(".task-card__metadata")?.textContent).toContain("Personal"); + expect(card.querySelector(".task-card__metadata")?.textContent).not.toContain("Calendar"); + }); + + it("refreshes the provider calendar name when an existing card updates", () => { + const plugin = createPlugin(); + const card = createICSEventCard(createEvent(), plugin as any); + plugin.calendarProviderRegistry.findProviderForEvent.mockReturnValue({ + providerName: "Microsoft Calendar", + extractEventIds: () => ({ calendarId: "work", eventId: "event-1" }), + getAvailableCalendars: () => [{ id: "work", summary: "Work" }], + }); + + updateICSEventCard( + card, + createEvent({ id: "microsoft-work-event-1", subscriptionId: "microsoft-work" }), + plugin as any + ); + + expect(card.querySelector(".task-card__metadata")?.textContent).toContain("Work"); + expect(card.querySelector(".task-card__metadata")?.textContent).not.toContain("Personal"); + }); + + it("uses the translated fallback when no subscription or provider owns the event", () => { + const plugin = createPlugin(); + plugin.calendarProviderRegistry.findProviderForEvent.mockReturnValue(undefined); + + const card = createICSEventCard( + createEvent({ id: "unknown-event", subscriptionId: "unknown" }), + plugin as any + ); + + expect(card.querySelector(".task-card__metadata")?.textContent).toContain("Calendar"); + }); +});