From b7c3a483772fe87dd2d216d0f769a60101b361eb Mon Sep 17 00:00:00 2001 From: Lorinc936 Date: Mon, 23 Mar 2026 17:25:22 +0100 Subject: [PATCH] feat: add Nextcloud notification provider fix: resolve hydration error in add-board modal --- packages/definitions/src/integration.ts | 2 +- .../src/nextcloud/nextcloud.integration.ts | 40 ++++++++++++++++++- .../src/boards/add-board-modal.tsx | 11 +++-- 3 files changed, 46 insertions(+), 7 deletions(-) diff --git a/packages/definitions/src/integration.ts b/packages/definitions/src/integration.ts index bbcfcde5f..06f6aa73f 100644 --- a/packages/definitions/src/integration.ts +++ b/packages/definitions/src/integration.ts @@ -217,7 +217,7 @@ export const integrationDefs = { name: "Nextcloud", secretKinds: [["username", "password"]], iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/nextcloud.svg", - category: ["calendar"], + category: ["calendar", "notifications"], documentationUrl: createDocumentationLink("/docs/integrations/nextcloud"), }, unifiController: { diff --git a/packages/integrations/src/nextcloud/nextcloud.integration.ts b/packages/integrations/src/nextcloud/nextcloud.integration.ts index aa57f480f..58e6b9024 100644 --- a/packages/integrations/src/nextcloud/nextcloud.integration.ts +++ b/packages/integrations/src/nextcloud/nextcloud.integration.ts @@ -6,7 +6,7 @@ import type { RequestInit as NodeFetchRequestInit } from "node-fetch"; import * as ical from "node-ical"; import { DAVClient } from "tsdav"; -import { createHttpsAgentAsync } from "@homarr/core/infrastructure/http"; +import { fetchWithTrustedCertificatesAsync, createHttpsAgentAsync } from "@homarr/core/infrastructure/http"; import { createLogger } from "@homarr/core/infrastructure/logs"; import { HandleIntegrationErrors } from "../base/errors/decorator"; @@ -16,6 +16,8 @@ import { Integration } from "../base/integration"; import type { TestingResult } from "../base/test-connection/test-connection-service"; import type { ICalendarIntegration } from "../interfaces/calendar/calendar-integration"; import type { CalendarEvent } from "../interfaces/calendar/calendar-types"; +import type { INotificationsIntegration } from "../interfaces/notifications/notifications-integration"; +import type { Notification } from "../interfaces/notifications/notification-types"; const logger = createLogger({ module: "nextcloudIntegration" }); @@ -23,7 +25,7 @@ dayjs.extend(utc); dayjs.extend(timezone); @HandleIntegrationErrors([integrationTsdavHttpErrorHandler]) -export class NextcloudIntegration extends Integration implements ICalendarIntegration { +export class NextcloudIntegration extends Integration implements ICalendarIntegration, INotificationsIntegration { protected async testingAsync(input: IntegrationTestingInput): Promise { const client = await this.createCalendarClientAsync(await createHttpsAgentAsync(input.options)); await client.login(); @@ -104,6 +106,40 @@ export class NextcloudIntegration extends Integration implements ICalendarIntegr .flat(); } + public async getNotificationsAsync(): Promise { + const url = this.url("/ocs/v2.php/apps/notifications/api/v2/notifications", { format: "json" }); + const response = await fetchWithTrustedCertificatesAsync(url, { + headers: { + Authorization: `Basic ${Buffer.from( + `${this.getSecretValue("username")}:${this.getSecretValue("password")}`, + ).toString("base64")}`, + "OCS-APIRequest": "true", + }, + }); + + if (!response.ok) { + throw new Error(`Nextcloud notifications request failed: ${response.status}`); + } + + const json = (await response.json()) as { + ocs: { + data: { + notification_id: number; + datetime: string; + subject: string; + message: string; + }[]; + }; + }; + + return json.ocs.data.map((n) => ({ + id: String(n.notification_id), + time: new Date(n.datetime), + title: n.subject, + body: n.message, + })); + } + private async createCalendarClientAsync(agent?: Agent) { return new DAVClient({ serverUrl: this.integration.url, diff --git a/packages/modals-collection/src/boards/add-board-modal.tsx b/packages/modals-collection/src/boards/add-board-modal.tsx index 919335b9b..9561a1717 100644 --- a/packages/modals-collection/src/boards/add-board-modal.tsx +++ b/packages/modals-collection/src/boards/add-board-modal.tsx @@ -55,14 +55,17 @@ export const AddBoardModal = createModal(({ actions }) => { label={t("board.field.name.label")} data-autofocus {...form.getInputProps("name")} - description={ - boardNameStatus.description ? ( + description={ + boardNameStatus.description ? ( + {boardNameStatus.description.icon ? : null} {boardNameStatus.description.label} - ) : null - } + + ) : null + } + />