fix(modal): close rollout and async lifecycle gaps

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-05 02:31:41 +08:00
co-authored by Claude Fable 5
parent 5375ad6429
commit aa865e24e1
8 changed files with 86 additions and 31 deletions
+5 -1
View File
@@ -10,11 +10,15 @@
var DEFAULT_TTL = 15000;
var entries = Object.create(null);
var flights = Object.create(null);
var lastScope = "";
function scope() {
var config = window.aksrtData || window.aksrt || {};
var userId = parseInt(config.userId || 0, 10) || 0;
return (config.isLoggedIn && userId > 0 ? "user:" + userId : "guest") + ":v1";
var current = (config.isLoggedIn && userId > 0 ? "user:" + userId : "guest") + ":v1";
if (lastScope && lastScope !== current) clear();
lastScope = current;
return current;
}
function token(key) {
+9 -4
View File
@@ -342,11 +342,12 @@
},
};
session.flight = cacheable
var flight = cacheable
? cache.acquire(cacheKey, request, acquireOptions)
: { promise: request(), release: function () {} };
session.flight = flight;
return session.flight.promise.then(function (result) {
return flight.promise.then(function (result) {
if (!session.closed && active === session) renderResult(session, result);
return session.shell;
}).catch(function (error) {
@@ -354,8 +355,12 @@
if (!aborted && !session.closed && active === session) renderError(session, error);
return session.shell;
}).then(function (shell) {
if (session.flight && typeof session.flight.release === "function") session.flight.release();
session.flight = null;
// A refresh/replacement may have installed a newer flight on this session.
// Only the owner of this promise may release and clear the slot.
if (session.flight === flight) {
if (flight && typeof flight.release === "function") flight.release();
session.flight = null;
}
return shell;
});
}
+37 -2
View File
@@ -13,6 +13,9 @@
var detailOrderNumber = "";
var detailAutoRefreshStarted = false;
var detailSession = null;
var detailReplacement = false;
var detailReplacementSession = null;
function toast(message, type) {
window.AksrtToast(message, type || "info");
@@ -48,7 +51,12 @@
}
function activeDetail(number) {
return detailOrderNumber === number && !!window.AksrtModalRuntime.getActive();
return detailOrderNumber === number
&& !!detailSession
&& !detailSession.closed
&& detailSession.options
&& detailSession.options.kind === "orders"
&& window.AksrtModalRuntime.getActive() === detailSession.shell;
}
function orderTag(orderNumber) {
@@ -115,8 +123,12 @@
cacheKey: "orders:detail:" + orderNumber,
cacheTags: ["orders", orderTag(orderNumber)],
skeleton: detailSkeleton(),
onState: function (_, session) {
if (orderNumber === detailOrderNumber) detailSession = session;
},
renderData: function (data, session) {
if (orderNumber !== detailOrderNumber) return false;
detailSession = session;
if (!data || typeof data !== "object") {
session.content.innerHTML = '<p class="c-red">订单不存在</p>';
return;
@@ -137,6 +149,9 @@
function openDetail(orderNumber) {
if (!orderNumber) return;
detailSession = null;
detailReplacement = false;
detailReplacementSession = null;
detailOrderNumber = orderNumber;
detailAutoRefreshStarted = false;
window.AksrtModalRuntime.openRemote(detailOptions(orderNumber, false));
@@ -144,7 +159,16 @@
function refreshDetail(orderNumber) {
if (!orderNumber || !activeDetail(orderNumber)) return;
window.AksrtModalRuntime.openRemote(detailOptions(orderNumber, true));
detailReplacement = true;
detailReplacementSession = detailSession;
var refreshPromise = window.AksrtModalRuntime.openRemote(detailOptions(orderNumber, true));
refreshPromise.then(function () {
detailReplacement = false;
detailReplacementSession = null;
}, function () {
detailReplacement = false;
detailReplacementSession = null;
});
}
function currentViewPath() {
@@ -301,6 +325,17 @@
if (cancel) { event.preventDefault(); closeOrder(cancel); }
});
document.addEventListener("aksrt:modal:close", function (event) {
var detail = event && event.detail ? event.detail : {};
if (!detailSession || detail.session !== detailSession) return;
if (detail.reason === "replace" && detailReplacement && detail.session === detailReplacementSession) return;
detailSession = null;
detailOrderNumber = "";
detailAutoRefreshStarted = false;
detailReplacement = false;
detailReplacementSession = null;
});
document.addEventListener("click", function (event) {
var refresh = event.target.closest("[data-aksrt-order-refresh]");
if (refresh) {
+17 -8
View File
@@ -16,6 +16,7 @@
var MAX_DELAY = 15000;
var createFlights = Object.create(null);
var polls = Object.create(null);
var lastScope = "";
var SUCCESS_STATUSES = {
paid: true,
delivered: true,
@@ -32,14 +33,16 @@
function userScope() {
var config = window.aksrtData || window.aksrt || {};
var userId = parseInt(config.userId || config.user_id || 0, 10) || 0;
if (userId > 0) return "user:" + userId;
return config.isLoggedIn ? "auth" : "guest";
var current = userId > 0 ? "user:" + userId : (config.isLoggedIn ? "auth" : "guest");
if (lastScope && lastScope !== current) clearFlights();
lastScope = current;
return current;
}
function clearFlights() {
createFlights = Object.create(null);
Object.keys(polls).forEach(function (orderNumber) {
var active = polls[orderNumber];
Object.keys(polls).forEach(function (pollKey) {
var active = polls[pollKey];
if (active && active.handle && typeof active.handle.abort === "function") active.handle.abort();
});
polls = Object.create(null);
@@ -121,6 +124,7 @@
function exchange(options) {
options = options || {};
userScope();
var packageId = parseInt(options.packageId || options.package_id || 0, 10);
if (!packageId) {
return Promise.resolve({ ok: false, status: 400, json: { message: "兑换套餐无效" } });
@@ -153,7 +157,9 @@
abort: function () {},
};
}
if (polls[orderNumber]) return polls[orderNumber].handle;
var pollScope = userScope();
var pollKey = pollScope + ":" + orderNumber;
if (polls[pollKey]) return polls[pollKey].handle;
var maxAttempts = Math.max(1, Number(options.maxAttempts || DEFAULT_MAX_ATTEMPTS));
var maxElapsed = Math.max(1000, Number(options.maxElapsed || DEFAULT_MAX_ELAPSED));
@@ -190,7 +196,7 @@
state.controller = null;
}
if (state.onVisibility) document.removeEventListener("visibilitychange", state.onVisibility);
if (polls[orderNumber] && polls[orderNumber].handle === state.handle) delete polls[orderNumber];
if (polls[pollKey] && polls[pollKey].handle === state.handle) delete polls[pollKey];
state.resolve(result);
}
@@ -226,6 +232,8 @@
function run() {
state.timer = null;
if (state.done || state.aborted) return;
// Detect an account transition even when no auth event reached this page.
if (userScope() !== pollScope || state.done || state.aborted) return;
if (document.hidden) {
state.waitingForVisible = true;
return;
@@ -289,7 +297,7 @@
finish({ terminal: false, aborted: true, status: "aborted" });
},
};
polls[orderNumber] = state;
polls[pollKey] = state;
schedule(0);
return state.handle;
}
@@ -349,7 +357,8 @@
poll: poll,
start: start,
abort: function (orderNumber) {
var active = polls[String(orderNumber || "")];
var key = userScope() + ":" + String(orderNumber || "");
var active = polls[key];
if (active && active.handle) active.handle.abort();
},
};