From 168b535953278cd61977148690e3bce3a4796ec7 Mon Sep 17 00:00:00 2001 From: CODEX Date: Tue, 23 Jun 2026 18:32:04 +0300 Subject: [PATCH] Sync BIM controls with live Launcher session --- frontend/dcViewer.js | 92 +++++++++++++++++++++++++++++++++++++++++++- server/index.js | 5 ++- 2 files changed, 95 insertions(+), 2 deletions(-) diff --git a/frontend/dcViewer.js b/frontend/dcViewer.js index 410eb7d..02d3ca6 100644 --- a/frontend/dcViewer.js +++ b/frontend/dcViewer.js @@ -186,6 +186,8 @@ const COMMENTS_API = "/api/comments"; const UPLOAD_VERSIONS_API = "/api/uploads/versions"; const SHARE_AUTH_GUEST_PARAM = "ndc_bim_guest"; const SHARE_PROMOTION_TIMEOUT_MS = 4500; +const SHARE_PROMOTION_RETRY_MS = 15000; +const SESSION_STATUS_WATCH_INTERVAL_MS = 2500; const NODEDC_SESSION_EVENT_TYPE = "nodedc:session:logout"; const acceptByType = { @@ -664,6 +666,8 @@ const isNodeDCSessionLogoutEvent = (value) => ( let nodeDCSessionSyncCleanup = null; let nodeDCLogoutHandling = false; +let nodeDCSessionWatchTimer = null; +let nodeDCSessionWatchInFlight = false; const handleNodeDCSessionLogout = async () => { if (nodeDCLogoutHandling) return; @@ -759,7 +763,8 @@ const scheduleShareSessionPromotion = (shareToken) => { let storageKey = ""; try { storageKey = `bimdc-share-promote:${shareToken}`; - if (sessionStorage.getItem(storageKey)) { + const lastAttempt = Number(sessionStorage.getItem(storageKey) || 0); + if (Number.isFinite(lastAttempt) && Date.now() - lastAttempt < SHARE_PROMOTION_RETRY_MS) { return; } sessionStorage.setItem(storageKey, String(Date.now())); @@ -786,6 +791,7 @@ const scheduleShareSessionPromotion = (shareToken) => { if (payload.type !== "nodedc:bim-share-promoted") return; done = true; cleanup(); + if (payload.promoted === false) return; window.location.replace(payload.nextPath || getCurrentReturnPath()); }; @@ -798,6 +804,89 @@ const scheduleShareSessionPromotion = (shareToken) => { document.body.appendChild(iframe); }; +const applyLiveSessionPayload = async (sessionPayload, { reason = "watch" } = {}) => { + const wasAuthenticated = runtimeSessionInfo.authenticated === true; + const isAuthenticated = sessionPayload?.authenticated === true; + + runtimeSessionInfo = { + ...runtimeSessionInfo, + ...(sessionPayload || {}), + authenticated: isAuthenticated, + canShare: sessionPayload?.canShare === true, + user: sessionPayload?.user || null, + }; + + if (!isAuthenticated) { + if (wasAuthenticated) { + await handleNodeDCSessionLogout(); + return; + } + const shareToken = getShareTokenFromPath(); + if (shareToken && viewerMode === "guest") { + scheduleShareSessionPromotion(shareToken); + } + return; + } + + if (getShareTokenFromPath() && viewerMode === "guest") { + viewerMode = "standard"; + } + applyRuntimeMode(); + updatePanelsVisibility(); + + if (reason === "focus" || reason === "visibility") { + loadProjectsList?.().catch(() => {}); + } +}; + +const refreshNodeDCSessionStatus = async (options = {}) => { + if (nodeDCSessionWatchInFlight || nodeDCLogoutHandling) return; + nodeDCSessionWatchInFlight = true; + try { + const response = await fetch(AUTH_SESSION_API, { + credentials: "include", + cache: "no-store", + headers: { "Accept": "application/json" }, + }); + if (response.ok) { + await applyLiveSessionPayload(await response.json(), options); + } + } catch (_) { + // A transient network failure must not log the user out client-side. + } finally { + nodeDCSessionWatchInFlight = false; + } +}; + +const setupNodeDCSessionWatcher = () => { + if (nodeDCSessionWatchTimer || typeof window === "undefined") return; + + nodeDCSessionWatchTimer = window.setInterval(() => { + void refreshNodeDCSessionStatus({ reason: "interval" }); + }, SESSION_STATUS_WATCH_INTERVAL_MS); + + window.addEventListener("focus", () => { + void refreshNodeDCSessionStatus({ reason: "focus" }); + const shareToken = getShareTokenFromPath(); + if (shareToken && viewerMode === "guest") { + scheduleShareSessionPromotion(shareToken); + } + }); + + window.addEventListener("pageshow", () => { + void refreshNodeDCSessionStatus({ reason: "pageshow" }); + }); + + document.addEventListener("visibilitychange", () => { + if (document.visibilityState !== "visible") return; + void refreshNodeDCSessionStatus({ reason: "visibility" }); + const shareToken = getShareTokenFromPath(); + if (shareToken && viewerMode === "guest") { + scheduleShareSessionPromotion(shareToken); + } + }); +}; + const COMMENT_TARGET_ICON = `