Beforeunload-Warnung + Vollbild-Button fuer Viewer
- beforeunload Event verhindert versehentliches Verlassen/Reload waehrend Broadcasting oder Viewing aktiv ist - Vollbild-Button im Viewer-Header (Fullscreen API) - Fullscreen-State wird korrekt getrackt und Icon wechselt Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
2ee36789b2
commit
3ef25fc10a
5 changed files with 109 additions and 51 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
4
web/dist/index.html
vendored
4
web/dist/index.html
vendored
|
|
@ -5,8 +5,8 @@
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Gaming Hub</title>
|
<title>Gaming Hub</title>
|
||||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🎮</text></svg>" />
|
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🎮</text></svg>" />
|
||||||
<script type="module" crossorigin src="/assets/index-C3FFX5uU.js"></script>
|
<script type="module" crossorigin src="/assets/index-B0xoTjru.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-CcoMcI3c.css">
|
<link rel="stylesheet" crossorigin href="/assets/index-BL4zgtRP.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|
|
||||||
|
|
@ -456,6 +456,37 @@ export default function StreamingTab({ data }: { data: any }) {
|
||||||
setViewing(null);
|
setViewing(null);
|
||||||
}, [cleanupViewer, wsSend]);
|
}, [cleanupViewer, wsSend]);
|
||||||
|
|
||||||
|
// ── Warn before leaving page while active ──
|
||||||
|
useEffect(() => {
|
||||||
|
const handler = (e: BeforeUnloadEvent) => {
|
||||||
|
if (isBroadcastingRef.current || viewingRef.current) {
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
window.addEventListener('beforeunload', handler);
|
||||||
|
return () => window.removeEventListener('beforeunload', handler);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// ── Fullscreen toggle for viewer ──
|
||||||
|
const viewerContainerRef = useRef<HTMLDivElement | null>(null);
|
||||||
|
const [isFullscreen, setIsFullscreen] = useState(false);
|
||||||
|
|
||||||
|
const toggleFullscreen = useCallback(() => {
|
||||||
|
const el = viewerContainerRef.current;
|
||||||
|
if (!el) return;
|
||||||
|
if (!document.fullscreenElement) {
|
||||||
|
el.requestFullscreen().catch(() => {});
|
||||||
|
} else {
|
||||||
|
document.exitFullscreen().catch(() => {});
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handler = () => setIsFullscreen(!!document.fullscreenElement);
|
||||||
|
document.addEventListener('fullscreenchange', handler);
|
||||||
|
return () => document.removeEventListener('fullscreenchange', handler);
|
||||||
|
}, []);
|
||||||
|
|
||||||
// ── Cleanup on unmount ──
|
// ── Cleanup on unmount ──
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
return () => {
|
return () => {
|
||||||
|
|
@ -473,7 +504,7 @@ export default function StreamingTab({ data }: { data: any }) {
|
||||||
if (viewing) {
|
if (viewing) {
|
||||||
const stream = streams.find(s => s.id === viewing.streamId);
|
const stream = streams.find(s => s.id === viewing.streamId);
|
||||||
return (
|
return (
|
||||||
<div className="stream-viewer-overlay">
|
<div className="stream-viewer-overlay" ref={viewerContainerRef}>
|
||||||
<div className="stream-viewer-header">
|
<div className="stream-viewer-header">
|
||||||
<div className="stream-viewer-header-left">
|
<div className="stream-viewer-header-left">
|
||||||
<span className="stream-live-badge"><span className="stream-live-dot" /> LIVE</span>
|
<span className="stream-live-badge"><span className="stream-live-dot" /> LIVE</span>
|
||||||
|
|
@ -484,8 +515,13 @@ export default function StreamingTab({ data }: { data: any }) {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="stream-viewer-header-right">
|
||||||
|
<button className="stream-viewer-fullscreen" onClick={toggleFullscreen} title={isFullscreen ? 'Vollbild verlassen' : 'Vollbild'}>
|
||||||
|
{isFullscreen ? '\u2716' : '\u26F6'}
|
||||||
|
</button>
|
||||||
<button className="stream-viewer-close" onClick={leaveViewing}>Verlassen</button>
|
<button className="stream-viewer-close" onClick={leaveViewing}>Verlassen</button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<div className="stream-viewer-video">
|
<div className="stream-viewer-video">
|
||||||
{viewing.phase === 'connecting' ? (
|
{viewing.phase === 'connecting' ? (
|
||||||
<div className="stream-viewer-connecting">
|
<div className="stream-viewer-connecting">
|
||||||
|
|
|
||||||
|
|
@ -229,6 +229,28 @@
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
}
|
}
|
||||||
|
.stream-viewer-header-right {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
.stream-viewer-fullscreen {
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
border: none;
|
||||||
|
color: #fff;
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
border-radius: var(--radius);
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 18px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
transition: background var(--transition);
|
||||||
|
}
|
||||||
|
.stream-viewer-fullscreen:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.25);
|
||||||
|
}
|
||||||
.stream-viewer-close {
|
.stream-viewer-close {
|
||||||
background: rgba(255, 255, 255, 0.1);
|
background: rgba(255, 255, 255, 0.1);
|
||||||
border: none;
|
border: none;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue