Fix hub iframe nav flicker with normal navigation and loading overlay
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -3,7 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
||||||
<script src="/static/instance_theme.js?v=13"></script>
|
<script src="/static/instance_theme.js?v=14"></script>
|
||||||
<link rel="stylesheet" href="/static/instance_theme_early.css?v=4">
|
<link rel="stylesheet" href="/static/instance_theme_early.css?v=4">
|
||||||
<link rel="stylesheet" href="/static/account_risk_badge.css?v=3">
|
<link rel="stylesheet" href="/static/account_risk_badge.css?v=3">
|
||||||
<script src="/static/account_risk_badge.js?v=3"></script>
|
<script src="/static/account_risk_badge.js?v=3"></script>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
||||||
<script src="/static/instance_theme.js?v=13"></script>
|
<script src="/static/instance_theme.js?v=14"></script>
|
||||||
<link rel="stylesheet" href="/static/instance_theme_early.css?v=4">
|
<link rel="stylesheet" href="/static/instance_theme_early.css?v=4">
|
||||||
<link rel="stylesheet" href="/static/account_risk_badge.css?v=3">
|
<link rel="stylesheet" href="/static/account_risk_badge.css?v=3">
|
||||||
<script src="/static/account_risk_badge.js?v=3"></script>
|
<script src="/static/account_risk_badge.js?v=3"></script>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
||||||
<script src="/static/instance_theme.js?v=13"></script>
|
<script src="/static/instance_theme.js?v=14"></script>
|
||||||
<link rel="stylesheet" href="/static/instance_theme_early.css?v=4">
|
<link rel="stylesheet" href="/static/instance_theme_early.css?v=4">
|
||||||
<link rel="stylesheet" href="/static/account_risk_badge.css?v=3">
|
<link rel="stylesheet" href="/static/account_risk_badge.css?v=3">
|
||||||
<script src="/static/account_risk_badge.js?v=3"></script>
|
<script src="/static/account_risk_badge.js?v=3"></script>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
||||||
<script src="/static/instance_theme.js?v=13"></script>
|
<script src="/static/instance_theme.js?v=14"></script>
|
||||||
<link rel="stylesheet" href="/static/instance_theme_early.css?v=4">
|
<link rel="stylesheet" href="/static/instance_theme_early.css?v=4">
|
||||||
<link rel="stylesheet" href="/static/account_risk_badge.css?v=3">
|
<link rel="stylesheet" href="/static/account_risk_badge.css?v=3">
|
||||||
<script src="/static/account_risk_badge.js?v=3"></script>
|
<script src="/static/account_risk_badge.js?v=3"></script>
|
||||||
|
|||||||
@@ -1189,6 +1189,10 @@ body.market-chart-fs-open {
|
|||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.instance-frame-shell.is-instance-nav-loading .instance-frame {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
.instance-frame-toolbar {
|
.instance-frame-toolbar {
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -429,6 +429,11 @@
|
|||||||
return j.url;
|
return j.url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setInstanceFrameNavLoading(loading) {
|
||||||
|
const shell = document.getElementById("instance-frame-shell");
|
||||||
|
if (shell) shell.classList.toggle("is-instance-nav-loading", !!loading);
|
||||||
|
}
|
||||||
|
|
||||||
async function openInstance(exchangeId, nextPath, opts) {
|
async function openInstance(exchangeId, nextPath, opts) {
|
||||||
const options = opts || {};
|
const options = opts || {};
|
||||||
const newTab = !!options.newTab;
|
const newTab = !!options.newTab;
|
||||||
@@ -481,7 +486,10 @@
|
|||||||
);
|
);
|
||||||
instanceFrameUrl = url;
|
instanceFrameUrl = url;
|
||||||
const frame = document.getElementById("instance-frame");
|
const frame = document.getElementById("instance-frame");
|
||||||
if (frame) frame.src = url;
|
if (frame) {
|
||||||
|
setInstanceFrameNavLoading(true);
|
||||||
|
frame.src = url;
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
showToast(String(e), true);
|
showToast(String(e), true);
|
||||||
}
|
}
|
||||||
@@ -498,6 +506,7 @@
|
|||||||
closeExchangeFullscreen();
|
closeExchangeFullscreen();
|
||||||
instanceFrameUrl = url;
|
instanceFrameUrl = url;
|
||||||
if (titleEl) titleEl.textContent = title || "实例";
|
if (titleEl) titleEl.textContent = title || "实例";
|
||||||
|
setInstanceFrameNavLoading(true);
|
||||||
frame.src = url;
|
frame.src = url;
|
||||||
shell.classList.remove("hidden");
|
shell.classList.remove("hidden");
|
||||||
shell.setAttribute("aria-hidden", "false");
|
shell.setAttribute("aria-hidden", "false");
|
||||||
@@ -528,6 +537,7 @@
|
|||||||
if (shell) {
|
if (shell) {
|
||||||
shell.classList.add("hidden");
|
shell.classList.add("hidden");
|
||||||
shell.setAttribute("aria-hidden", "true");
|
shell.setAttribute("aria-hidden", "true");
|
||||||
|
shell.classList.remove("is-instance-nav-loading");
|
||||||
}
|
}
|
||||||
document.body.classList.remove("hub-instance-frame-open");
|
document.body.classList.remove("hub-instance-frame-open");
|
||||||
}
|
}
|
||||||
@@ -3332,6 +3342,23 @@
|
|||||||
const back = document.getElementById("instance-frame-back");
|
const back = document.getElementById("instance-frame-back");
|
||||||
const refresh = document.getElementById("instance-frame-refresh");
|
const refresh = document.getElementById("instance-frame-refresh");
|
||||||
const newTab = document.getElementById("instance-frame-newtab");
|
const newTab = document.getElementById("instance-frame-newtab");
|
||||||
|
const frame = document.getElementById("instance-frame");
|
||||||
|
if (frame && frame.dataset.hubNavBound !== "1") {
|
||||||
|
frame.dataset.hubNavBound = "1";
|
||||||
|
frame.addEventListener("load", () => setInstanceFrameNavLoading(false));
|
||||||
|
}
|
||||||
|
if (!window.__hubInstanceFrameMsgBound) {
|
||||||
|
window.__hubInstanceFrameMsgBound = true;
|
||||||
|
window.addEventListener("message", (ev) => {
|
||||||
|
const d = ev.data;
|
||||||
|
if (!d || typeof d !== "object") return;
|
||||||
|
if (d.type === "instance-frame-navigating") {
|
||||||
|
setInstanceFrameNavLoading(true);
|
||||||
|
} else if (d.type === "instance-frame-ready" || d.type === "instance-theme-ready") {
|
||||||
|
setInstanceFrameNavLoading(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
if (back) back.onclick = () => closeInstanceFrame();
|
if (back) back.onclick = () => closeInstanceFrame();
|
||||||
if (refresh) refresh.onclick = () => refreshInstanceFrame();
|
if (refresh) refresh.onclick = () => refreshInstanceFrame();
|
||||||
if (newTab) {
|
if (newTab) {
|
||||||
|
|||||||
+23
-31
@@ -329,41 +329,33 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 仅中控 iframe 内:fetch + document.write 换页,避免 iframe 整页卸载白屏。单独打开实例仍走浏览器正常跳转。 */
|
function notifyParentFrameNavStart() {
|
||||||
|
if (!isHubLinked()) return;
|
||||||
|
try {
|
||||||
|
window.parent.postMessage({ type: "instance-frame-navigating", theme: get() }, "*");
|
||||||
|
} catch (_) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
function notifyParentFrameReady() {
|
||||||
|
if (!isHubLinked()) return;
|
||||||
|
try {
|
||||||
|
window.parent.postMessage({ type: "instance-frame-ready", theme: get() }, "*");
|
||||||
|
} catch (_) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 中控 iframe:顶栏用正常跳转(与单独打开实例一致),由中控 shell 遮罩盖住 iframe 加载过程。 */
|
||||||
function initHubEmbedInFrameNav() {
|
function initHubEmbedInFrameNav() {
|
||||||
if (!isHubLinked()) return;
|
if (!isHubLinked()) return;
|
||||||
|
|
||||||
let navToken = 0;
|
|
||||||
|
|
||||||
function isSoftNavLink(a) {
|
function isSoftNavLink(a) {
|
||||||
if (!a || !a.getAttribute) return false;
|
if (!a || !a.getAttribute) return false;
|
||||||
|
if (a.hasAttribute("download") || a.target === "_blank") return false;
|
||||||
return !!a.closest(".top-nav, .strategy-subnav");
|
return !!a.closest(".top-nav, .strategy-subnav");
|
||||||
}
|
}
|
||||||
|
|
||||||
async function navigateInFrame(href, opts) {
|
function navigateTopNav(href) {
|
||||||
const token = ++navToken;
|
notifyParentFrameNavStart();
|
||||||
try {
|
location.assign(href);
|
||||||
const r = await fetch(href, { credentials: "same-origin" });
|
|
||||||
if (token !== navToken) return;
|
|
||||||
if (!r.ok) {
|
|
||||||
location.href = href;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const html = await r.text();
|
|
||||||
if (token !== navToken) return;
|
|
||||||
let path = href;
|
|
||||||
try {
|
|
||||||
const u = new URL(href, location.href);
|
|
||||||
path = u.pathname + u.search + u.hash;
|
|
||||||
} catch (_) {}
|
|
||||||
if (opts && opts.replace) history.replaceState(null, "", path);
|
|
||||||
else history.pushState(null, "", path);
|
|
||||||
document.open();
|
|
||||||
document.write(html);
|
|
||||||
document.close();
|
|
||||||
} catch (_) {
|
|
||||||
if (token === navToken) location.href = href;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
document.addEventListener(
|
document.addEventListener(
|
||||||
@@ -384,14 +376,12 @@
|
|||||||
const nextHref = target.pathname + target.search + target.hash;
|
const nextHref = target.pathname + target.search + target.hash;
|
||||||
if (target.pathname === location.pathname && target.search === location.search) return;
|
if (target.pathname === location.pathname && target.search === location.search) return;
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
void navigateInFrame(nextHref);
|
navigateTopNav(nextHref);
|
||||||
},
|
},
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
|
|
||||||
window.addEventListener("popstate", () => {
|
window.addEventListener("pagehide", notifyParentFrameNavStart);
|
||||||
void navigateInFrame(location.pathname + location.search + location.hash, { replace: true });
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function purgeLegacySoftNavCache() {
|
function purgeLegacySoftNavCache() {
|
||||||
@@ -418,6 +408,7 @@
|
|||||||
apply(get(), { skipStore: true });
|
apply(get(), { skipStore: true });
|
||||||
window.addEventListener("message", (ev) => initFromHubMessage(ev.data));
|
window.addEventListener("message", (ev) => initFromHubMessage(ev.data));
|
||||||
initHubEmbedInFrameNav();
|
initHubEmbedInFrameNav();
|
||||||
|
notifyParentFrameReady();
|
||||||
try {
|
try {
|
||||||
window.parent.postMessage({ type: "instance-theme-ready" }, "*");
|
window.parent.postMessage({ type: "instance-theme-ready" }, "*");
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
@@ -447,6 +438,7 @@
|
|||||||
syncInlineStyles(get());
|
syncInlineStyles(get());
|
||||||
patchHubNavLinks(get());
|
patchHubNavLinks(get());
|
||||||
observeDynamicLists();
|
observeDynamicLists();
|
||||||
|
if (isHubLinked()) notifyParentFrameReady();
|
||||||
};
|
};
|
||||||
if (document.readyState === "loading") {
|
if (document.readyState === "loading") {
|
||||||
document.addEventListener("DOMContentLoaded", onReady);
|
document.addEventListener("DOMContentLoaded", onReady);
|
||||||
|
|||||||
Reference in New Issue
Block a user