diff --git a/apps/web/src/app/(portal)/modules/nextcloud-files/components/ConnectPanel.tsx b/apps/web/src/app/(portal)/modules/nextcloud-files/components/ConnectPanel.tsx index 72f0c1d..fdb1a2c 100644 --- a/apps/web/src/app/(portal)/modules/nextcloud-files/components/ConnectPanel.tsx +++ b/apps/web/src/app/(portal)/modules/nextcloud-files/components/ConnectPanel.tsx @@ -47,7 +47,8 @@ export function ConnectPanel({ /** Name, Farbe und Logo der Nextcloud; solange sie fehlen, steht nur der Rechnername da. */ info?: NextcloudServerInfo | null; expired: boolean; - onConnected: () => void; + /** Verbunden (oder Stand pruefen): die Seite laedt den Stand neu. */ + onConnected: () => void | Promise; }) { const t = useTranslations('nextcloudFiles.connect'); @@ -182,7 +183,18 @@ export function ConnectPanel({ if (err instanceof NextcloudFilesRequestError) { if (err.status === 404 || err.status === 410) { // Abgelaufen oder unbekannt (z. B. nach einem Neustart der API): von vorn beginnen. - stop({ code: 'flowExpired', message: err.message, retryAfterSeconds: null }); + // Vorher den Stand neu laden (IN-05): ging die Antwort "verbunden" unterwegs verloren, + // ist das Konto in Wahrheit verbunden und die Seite zeigt gleich die Dateien. + stopped = true; + const expired: ErrorState = { + code: 'flowExpired', + message: err.message, + retryAfterSeconds: null, + }; + void Promise.resolve() + .then(() => onConnectedRef.current()) + .catch(() => undefined) + .finally(() => stop(expired)); } else if (err.code === 'nextcloudLocked' || (err.status < 500 && err.status !== 429)) { stop(toErrorRef.current(err)); } diff --git a/apps/web/src/app/(portal)/modules/nextcloud-files/nextcloud-files-page.test.tsx b/apps/web/src/app/(portal)/modules/nextcloud-files/nextcloud-files-page.test.tsx index e3c276f..772a98d 100644 --- a/apps/web/src/app/(portal)/modules/nextcloud-files/nextcloud-files-page.test.tsx +++ b/apps/web/src/app/(portal)/modules/nextcloud-files/nextcloud-files-page.test.tsx @@ -497,6 +497,21 @@ describe('Browser-Anmeldung', () => { expect(screen.queryByRole('link', { name: 'Anmeldung bei Nextcloud öffnen' })).toBeNull(); }); + it('410/404 nach verlorener "verbunden"-Antwort: erst den Stand pruefen, dann die Dateien statt "abgelaufen" (IN-05)', async () => { + await startFlow(); + const before = mockGetStatus.mock.calls.length; + mockPollFlow.mockRejectedValue(new NextcloudFilesRequestError(404, 'notFound', 'x')); + mockGetStatus.mockResolvedValue(status({ account: account({ displayName: 'Zwei Faktor' }) })); + await act(async () => { + await vi.advanceTimersByTimeAsync(2000); + }); + expect(await screen.findByText('Angemeldet als Zwei Faktor')).toBeTruthy(); + expect(mockGetStatus.mock.calls.length).toBeGreaterThan(before); + expect( + screen.queryByText('Die Anmeldung ist abgelaufen. Bitte starten Sie sie neu.'), + ).toBeNull(); + }); + it('"failed" beendet die Abfrage und zeigt den Text', async () => { await startFlow(); mockPollFlow.mockResolvedValue({