From 4c7987427895ae53d47c94f44a9cc8f13b2429b1 Mon Sep 17 00:00:00 2001 From: Schalli Date: Thu, 17 Sep 2026 14:52:47 +0200 Subject: [PATCH] =?UTF-8?q?feat(web):=20Einstellungen=20=E2=86=92=20Deskto?= =?UTF-8?q?p-App=20zeigt=20in=20der=20App=20den=20verbundenen=20Server?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - DesktopAppSettings rendert im Desktop-Client (useIsDesktopClient) einen Block "Verbunden mit: {origin}" plus Hinweis auf "Server-Adresse ändern…" im Infobereich-Menü; im Browser bleibt der Block weg - Neue i18n-Schlüssel settings.desktop.connectedTo/changeHint in de.json und en.json - Test 4 (Cookie tessera_desktop=1) und Test 5 (ohne Cookie) ergänzt; RED zuerst (Test 4 schlug auf der Zielbehauptung fehl), dann GREEN - Alle 431 Web-Tests und type-check grün Co-Authored-By: Claude Opus 5 (1M context) --- .../settings/desktop-app-settings.test.tsx | 28 ++++++++++++++++++- .../settings/desktop-app-settings.tsx | 19 ++++++++++++- apps/web/src/messages/de.json | 4 ++- apps/web/src/messages/en.json | 4 ++- 4 files changed, 51 insertions(+), 4 deletions(-) diff --git a/apps/web/src/components/settings/desktop-app-settings.test.tsx b/apps/web/src/components/settings/desktop-app-settings.test.tsx index 0cdd742..9c3b846 100644 --- a/apps/web/src/components/settings/desktop-app-settings.test.tsx +++ b/apps/web/src/components/settings/desktop-app-settings.test.tsx @@ -1,5 +1,6 @@ -import { cleanup, render, screen } from '@testing-library/react'; +import { act, cleanup, render, screen } from '@testing-library/react'; import { afterEach, describe, expect, it, vi } from 'vitest'; +import { DESKTOP_COOKIE_NAME } from '@/lib/desktop-client'; import { DesktopAppSettings } from './desktop-app-settings'; /** @@ -34,9 +35,14 @@ vi.mock('@/lib/desktop', async (importOriginal) => { }; }); +function clearDesktopCookie() { + document.cookie = `${DESKTOP_COOKIE_NAME}=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/`; +} + afterEach(() => { cleanup(); vi.clearAllMocks(); + clearDesktopCookie(); }); const windowsFile = { @@ -102,4 +108,24 @@ describe('DesktopAppSettings (18-03)', () => { ), ).toBeInTheDocument(); }); + + it('Test 4 (im Desktop-Client): zeigt verbundenen Server und Änderungshinweis', async () => { + document.cookie = `${DESKTOP_COOKIE_NAME}=1; path=/`; + loadDesktopLatest.mockResolvedValue(null); + render(); + + expect(await screen.findByText('Verbunden mit: http://localhost:3000')).toBeInTheDocument(); + expect( + screen.getByText('Ändern über das Tessera-Symbol im Infobereich → „Server-Adresse ändern…“'), + ).toBeInTheDocument(); + expect(screen.getByTestId('desktop-connected')).toBeInTheDocument(); + }); + + it('Test 5 (im Browser): kein Block ohne Desktop-Cookie', async () => { + loadDesktopLatest.mockResolvedValue(null); + render(); + await act(async () => {}); + + expect(screen.queryByTestId('desktop-connected')).toBeNull(); + }); }); diff --git a/apps/web/src/components/settings/desktop-app-settings.tsx b/apps/web/src/components/settings/desktop-app-settings.tsx index 7726d97..491d654 100644 --- a/apps/web/src/components/settings/desktop-app-settings.tsx +++ b/apps/web/src/components/settings/desktop-app-settings.tsx @@ -3,6 +3,7 @@ import { useEffect, useState } from 'react'; import { useLocale, useTranslations } from 'next-intl'; import { desktopDownloadUrl, type DesktopFileInfo, type DesktopLatestInfo, formatFileSize, loadDesktopLatest } from '@/lib/desktop'; +import { useIsDesktopClient } from '@/lib/desktop-client'; const BUTTON_CLASS = 'inline-flex items-center gap-2 rounded-md bg-primary px-4 py-2.5 text-sm font-medium text-primary-foreground hover:opacity-90'; @@ -59,11 +60,17 @@ function DownloadButton({ /** * Inhalt der Einstellungsseite "Desktop-App" (D-12): vier erklaerende * Saetze, dann Version/Downloads oder ein Hinweis, wenn der Server keine - * Pakete traegt (`loadDesktopLatest()` liefert `null`). + * Pakete traegt (`loadDesktopLatest()` liefert `null`). Im Desktop-Client + * (260917-jn2) zusaetzlich ein Block "Verbunden mit: {origin}" -- `isDesktop` + * ist im Server-HTML und im ersten Client-Render false (Hook mit + * `useEffect`), der Block rendert also erst nach der Hydration, weshalb + * `window.location.origin` hier direkt gelesen werden darf (kein + * Hydration-Fehler). */ export function DesktopAppSettings() { const t = useTranslations('settings'); const locale = useLocale(); + const isDesktop = useIsDesktopClient(); const [info, setInfo] = useState(undefined); useEffect(() => { @@ -85,6 +92,16 @@ export function DesktopAppSettings() {

{t('desktop.tray')}

{t('desktop.update')}

+ {isDesktop && ( +
+

{t('desktop.connectedTo', { origin: window.location.origin })}

+

{t('desktop.changeHint')}

+
+ )} + {info === null &&

{t('desktop.unavailable')}

} {info && ( diff --git a/apps/web/src/messages/de.json b/apps/web/src/messages/de.json index 88d73e9..a7b88e6 100644 --- a/apps/web/src/messages/de.json +++ b/apps/web/src/messages/de.json @@ -169,7 +169,9 @@ "downloadWindows": "Für Windows herunterladen", "downloadLinux": "Für Linux herunterladen", "fileInfo": "{name} · {size}", - "unavailable": "Auf diesem Server sind derzeit keine Desktop-Pakete hinterlegt." + "unavailable": "Auf diesem Server sind derzeit keine Desktop-Pakete hinterlegt.", + "connectedTo": "Verbunden mit: {origin}", + "changeHint": "Ändern über das Tessera-Symbol im Infobereich → „Server-Adresse ändern…“" }, "sourceDeleteConfirm": "Möchten Sie diese Kalenderquelle wirklich löschen?", "sourceDeleteCta": "Quelle löschen", diff --git a/apps/web/src/messages/en.json b/apps/web/src/messages/en.json index 86fbd3e..96d909d 100644 --- a/apps/web/src/messages/en.json +++ b/apps/web/src/messages/en.json @@ -169,7 +169,9 @@ "downloadWindows": "Download for Windows", "downloadLinux": "Download for Linux", "fileInfo": "{name} · {size}", - "unavailable": "No desktop packages are available on this server yet." + "unavailable": "No desktop packages are available on this server yet.", + "connectedTo": "Connected to: {origin}", + "changeHint": "To change it, use the Tessera icon in the notification area → “Change server address…”" }, "sourceDeleteConfirm": "Are you sure you want to delete this calendar source?", "sourceDeleteCta": "Delete source",