187fb76c91
- release-notes.ts: reine Auswahl der Versionsabschnitte aus CHANGELOG.md
- release-notice-actions.ts ('use server'): Abruf und Merken ueber die API
- ReleaseNoticeDialog: barrierefreies Fenster, Eintraege ueber ChangelogView (variant plain)
- ReleaseNoticeHost in AppShell: einmal je Seitenladung, merkt erst beim Schliessen
- Texte releaseNotice in de.json und en.json
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
70 lines
2.5 KiB
TypeScript
70 lines
2.5 KiB
TypeScript
'use client';
|
|
|
|
import { usePathname } from 'next/navigation';
|
|
import { lazy, Suspense, useEffect, useRef, useState } from 'react';
|
|
import type { ReleaseNotice } from '@/lib/release-notes';
|
|
import { fetchReleaseNotice, markReleaseSeenAction } from '@/lib/release-notice-actions';
|
|
|
|
/**
|
|
* Laedt das "Was ist neu"-Fenster einmal je Seitenladung im Portal-Rahmen
|
|
* (quick-260925-bow, D-03/D-05/D-08).
|
|
*
|
|
* Eingebunden nur in `AppShell` — die Anmeldeseite liegt im `(auth)`-Layout
|
|
* ohne AppShell, der Erststart-Dialog der Desktop-App ist die lokale
|
|
* `apps/desktop/src/setup.html` vor dem Portal. Auf `/change-password`
|
|
* (liegt im Portal-Rahmen, Pflicht-Passwortwechsel) wird nicht abgefragt;
|
|
* erst die naechste Seite danach fragt. Eine Ref-Sperre verhindert einen
|
|
* zweiten Abruf (StrictMode-Doppeleffekt, Seitenwechsel ohne Neuladen).
|
|
*
|
|
* Gemerkt wird erst beim Schliessen, nie beim Oeffnen: zuerst verschwindet
|
|
* das Fenster, dann geht `markReleaseSeenAction` an die API. Schlaegt das
|
|
* Merken fehl, erscheint das Fenster beim naechsten Laden erneut — gewollt,
|
|
* statt die Nachricht stumm zu verlieren.
|
|
*
|
|
* Das Fenster (mit dem Markdown-Renderer) wird per `React.lazy` nur geladen,
|
|
* wenn wirklich eine Nachricht da ist.
|
|
*/
|
|
|
|
const ReleaseNoticeDialog = lazy(() =>
|
|
import('./release-notice-dialog').then((mod) => ({ default: mod.ReleaseNoticeDialog })),
|
|
);
|
|
|
|
export function ReleaseNoticeHost() {
|
|
const pathname = usePathname();
|
|
const [notice, setNotice] = useState<ReleaseNotice | null>(null);
|
|
const requestedRef = useRef(false);
|
|
|
|
useEffect(() => {
|
|
if (requestedRef.current) return;
|
|
if (pathname?.startsWith('/change-password')) return;
|
|
requestedRef.current = true;
|
|
|
|
// Bewusst ohne Abbruch-Flag im Aufraeumen: der StrictMode-Doppeleffekt
|
|
// raeumt nach dem ersten Lauf auf, der zweite Lauf fragt wegen der Sperre
|
|
// nicht erneut — ein Flag wuerde das einzige Ergebnis verwerfen.
|
|
fetchReleaseNotice()
|
|
.then((result) => {
|
|
if (result) setNotice(result);
|
|
})
|
|
.catch(() => {
|
|
// Still: ohne Nachricht kein Fenster.
|
|
});
|
|
}, [pathname]);
|
|
|
|
if (!notice) return null;
|
|
|
|
const handleClose = () => {
|
|
const version = notice.currentRelease;
|
|
setNotice(null);
|
|
void markReleaseSeenAction(version).catch(() => {
|
|
// Still: das Fenster erscheint beim naechsten Laden erneut.
|
|
});
|
|
};
|
|
|
|
return (
|
|
<Suspense fallback={null}>
|
|
<ReleaseNoticeDialog notice={notice} onClose={handleClose} />
|
|
</Suspense>
|
|
);
|
|
}
|