Skip to content

Commit 16f2b99

Browse files
authored
Merge pull request #365 from project-mikan/fix/white-pwa
fix PWAで白くなるのを防ぐ
2 parents dc9d916 + 390d7d3 commit 16f2b99

3 files changed

Lines changed: 45 additions & 3 deletions

File tree

frontend/src/lib/components/PWAUpdateNotification.svelte

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,18 @@
1313
updateServiceWorker = registerSW({
1414
immediate: true,
1515
onNeedRefresh() {
16-
showUpdatePrompt = true;
16+
// スタンドアロン(PWA)モードの場合は自動リロードして白画面を防ぐ
17+
if (window.matchMedia("(display-mode: standalone)").matches) {
18+
updateServiceWorker?.()
19+
.then(() => {
20+
window.location.reload();
21+
})
22+
.catch(() => {
23+
window.location.reload();
24+
});
25+
} else {
26+
showUpdatePrompt = true;
27+
}
1728
},
1829
onOfflineReady() {
1930
// PWA はオフライン使用の準備が完了しました

frontend/src/routes/+layout.svelte

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,38 @@
2424
2525
let lastActiveTime = Date.now();
2626
27+
const STALE_CACHE_THRESHOLD_MS = 30 * 60 * 1000;
28+
const TOKEN_REFRESH_THRESHOLD_MS = 5 * 60 * 1000;
29+
30+
// フォーム入力中かどうかを確認(リロード前のデータロスを防ぐ)
31+
function isEditingForm(): boolean {
32+
const el = document.activeElement;
33+
if (!el) return false;
34+
const tag = el.tagName.toLowerCase();
35+
return (
36+
tag === "input" ||
37+
tag === "textarea" ||
38+
tag === "select" ||
39+
(el as HTMLElement).isContentEditable
40+
);
41+
}
42+
43+
// キャッシュが古くなっている場合にリロードする(フォーム編集中はスキップ)
44+
function reloadIfStale(inactiveTime: number): boolean {
45+
if (inactiveTime > STALE_CACHE_THRESHOLD_MS && !isEditingForm()) {
46+
window.location.reload();
47+
return true;
48+
}
49+
return false;
50+
}
51+
2752
// ページがフォーカスされた時にトークンをチェック
2853
function handleVisibilityChange() {
2954
if (browser && document.visibilityState === "visible") {
3055
const inactiveTime = Date.now() - lastActiveTime;
56+
if (reloadIfStale(inactiveTime)) return;
3157
// 5分以上非アクティブだった場合、トークンをリフレッシュ
32-
if (inactiveTime > 5 * 60 * 1000 && isAuthenticated && !isAuthPage) {
58+
if (inactiveTime > TOKEN_REFRESH_THRESHOLD_MS && isAuthenticated && !isAuthPage) {
3359
invalidateAll();
3460
}
3561
lastActiveTime = Date.now();
@@ -40,8 +66,9 @@
4066
function handleFocus() {
4167
if (!browser) return;
4268
const inactiveTime = Date.now() - lastActiveTime;
69+
if (reloadIfStale(inactiveTime)) return;
4370
// 5分以上非アクティブだった場合、トークンをリフレッシュ
44-
if (inactiveTime > 5 * 60 * 1000 && isAuthenticated && !isAuthPage) {
71+
if (inactiveTime > TOKEN_REFRESH_THRESHOLD_MS && isAuthenticated && !isAuthPage) {
4572
invalidateAll();
4673
}
4774
lastActiveTime = Date.now();

frontend/vite.config.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ export default defineConfig({
1919
process.env.NODE_ENV === "development" ? "development" : "production",
2020
workbox: {
2121
globPatterns: ["**/*.{js,css,html,ico,png,svg}"],
22+
// 新しいSWが即時アクティベートしてすべてのクライアントを制御する
23+
// これにより久々に開いた際に古いキャッシュが残ることを防ぐ
24+
skipWaiting: true,
25+
clientsClaim: true,
2226
runtimeCaching: [
2327
{
2428
// SvelteKitのナビゲーションリクエスト(HTMLページ)をキャッシュ

0 commit comments

Comments
 (0)