Skip to content

Commit e8d5123

Browse files
toomoreclaude
andauthored
fix(pwa): 離線閱讀頁誤判成「這個瀏覽器沒有離線儲存」 (#272)
#269 用「等 1200 毫秒再問 getRegistration()」判斷這個環境有沒有 service worker。 那個延遲太短:Chrome 會把 register() 排到頁面 load 之後才真的跑,而這一頁要載完 material 的 bundle 與字型,正式站實測到十二秒才問得出 registration。 500ms registration=undefined 1500ms registration=false 6000ms registration=false 12000ms registration=true 結果是一個好好的瀏覽器被告知「這個瀏覽器沒有提供離線儲存」,章節清單的已存比例 全部顯示 0,勾選框也停用,等於整頁的功能都被關掉。 改成輪詢等 registration 出現,上限三十秒。真的不註冊的環境不靠等待判斷:base.html 是唯一知道要不要註冊的地方(hostname 白名單),讓它把結論寫進 window.__anoniServiceWorker,管理頁看到 false 就直接放棄。那支在 body 尾端比管理頁 的程式晚執行,所以第一輪拿到 undefined 是正常的,繼續等就是。 兩種環境都實測過:localhost 兩秒內顯示完整狀態,127.0.0.2(不在白名單)立刻顯示 說明,不空等三十秒。 Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
1 parent 81885ca commit e8d5123

2 files changed

Lines changed: 32 additions & 11 deletions

File tree

docs/overrides/base.html

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,10 @@
280280
#}
281281
<script>
282282
(function () {
283+
// 這個環境到底會不會註冊 service worker,只有這裡知道。離線內容管理頁
284+
// 靠這個旗標判斷要不要等,不然它只能空等到超時,才對著一個根本不會註冊的
285+
// 瀏覽器說「這裡沒有離線儲存」。
286+
window.__anoniServiceWorker = false;
283287
if (!("serviceWorker" in navigator)) return;
284288

285289
var script, options;
@@ -293,6 +297,7 @@
293297
} else {
294298
return;
295299
}
300+
window.__anoniServiceWorker = true;
296301

297302
// 換版提示的文案。document.documentElement.lang 在 zh-CN 版是 "zh"
298303
// (mkdocs_cn.yml 的 theme.language),對不到 zh-CN 這個代碼。

docs/zh-TW/js/offline-library.js

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -228,24 +228,40 @@
228228
return;
229229
}
230230

231-
// service worker 準備好了沒。
231+
// 等 registration 出現。
232+
//
233+
// 不能只等一小段固定時間就下判斷。Chrome 會把 register() 排到頁面 load 之後才真的
234+
// 跑,而這一頁要載完 material 的 bundle 與字型,正式站實測 getRegistration() 到
235+
// 十二秒才回得出東西。等太短就會對著一個好好的瀏覽器說「這裡沒有離線儲存」。
236+
//
237+
// 反過來,真的不註冊的環境(onion 版、IPFS gateway、停用 Service Worker 的瀏覽器)
238+
// 由 base.html 把 window.__anoniServiceWorker 設成 false,看到就直接放棄,不用空等。
239+
// 那支在 body 尾端,比這支晚執行,所以第一輪拿到 undefined 是正常的,繼續等就是。
240+
function waitForRegistration() {
241+
const deadline = Date.now() + 30000;
242+
const attempt = () => {
243+
if (window.__anoniServiceWorker === false) return Promise.resolve(null);
244+
return navigator.serviceWorker.getRegistration().then((registration) => {
245+
if (registration) return registration;
246+
if (Date.now() > deadline) return null;
247+
return new Promise((resolve) => setTimeout(resolve, 500)).then(attempt);
248+
});
249+
};
250+
return attempt();
251+
}
252+
253+
// service worker 可以收指令了沒。
232254
//
233255
// 不能直接用 navigator.serviceWorker.ready:首次造訪時 install 要把整個語系的核心
234256
// 章節抓完(約十 MB)才會 activate,ready 也才 resolve,行動網路上那是好幾分鐘。
235257
// 這一頁的清單不需要等那個,所以拆成兩段,索引先畫、狀態晚點補。
236-
//
237-
// 先讓出一輪再問有沒有 registration:base.html 的註冊碼在 body 尾端,比這支晚執行,
238-
// 太早問會拿到 undefined。問得到才等 ready,問不到就是這個環境不註冊(onion 版與
239-
// 停用 Service Worker 的瀏覽器),沒必要一直等下去。
240258
let readyPromise = null;
241259
function whenReady() {
242260
if (!readyPromise) {
243-
readyPromise = new Promise((resolve) => setTimeout(resolve, 1200))
244-
.then(() => navigator.serviceWorker.getRegistration())
245-
.then((registration) => {
246-
if (!registration) throw new Error("no-service-worker-registration");
247-
return navigator.serviceWorker.ready;
248-
});
261+
readyPromise = waitForRegistration().then((registration) => {
262+
if (!registration) throw new Error("no-service-worker-registration");
263+
return navigator.serviceWorker.ready;
264+
});
249265
}
250266
return readyPromise;
251267
}

0 commit comments

Comments
 (0)