feat(260925-bow): Was-ist-neu-Fenster im Portal-Rahmen
- 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>
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
'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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user