Skip to content

Commit f845564

Browse files
committed
feat: code by Trae
1 parent 3f02376 commit f845564

5 files changed

Lines changed: 78 additions & 62 deletions

File tree

epub_browser/assets/library.js

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,41 @@ function initScript() {
312312
});
313313
}
314314

315+
// PWA 安装提示
316+
let deferredPrompt;
317+
const installBtn = document.createElement('button');
318+
installBtn.id = 'pwa-install-btn';
319+
installBtn.innerHTML = '<i class="fas fa-download"></i> Install App';
320+
installBtn.style.cssText = 'position:fixed;bottom:20px;right:20px;padding:12px 20px;background:#4a90d9;color:#fff;border:none;border-radius:8px;cursor:pointer;font-size:14px;box-shadow:0 2px 10px rgba(0,0,0,0.2);z-index:9999;display:none;';
321+
document.body.appendChild(installBtn);
322+
323+
window.addEventListener('beforeinstallprompt', function(e) {
324+
e.preventDefault();
325+
deferredPrompt = e;
326+
installBtn.style.display = 'block';
327+
console.log('PWA install prompt captured');
328+
});
329+
330+
installBtn.addEventListener('click', function() {
331+
if (deferredPrompt) {
332+
installBtn.style.display = 'none';
333+
deferredPrompt.prompt();
334+
deferredPrompt.userChoice.then(function(choiceResult) {
335+
if (choiceResult.outcome === 'accepted') {
336+
console.log('User accepted the install prompt');
337+
} else {
338+
console.log('User dismissed the install prompt');
339+
}
340+
deferredPrompt = null;
341+
});
342+
}
343+
});
344+
345+
window.addEventListener('appinstalled', function() {
346+
installBtn.style.display = 'none';
347+
console.log('PWA installed successfully');
348+
});
349+
315350
};
316351

317352
// 如果DOM已经加载完成,立即初始化

epub_browser/assets/manifest.json

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,29 @@
1212
"src": "/assets/icon-192.png",
1313
"sizes": "192x192",
1414
"type": "image/png",
15-
"purpose": "any maskable"
15+
"purpose": "any"
1616
},
1717
{
1818
"src": "/assets/icon-512.png",
1919
"sizes": "512x512",
2020
"type": "image/png",
21-
"purpose": "any maskable"
21+
"purpose": "any"
22+
}
23+
],
24+
"screenshots": [
25+
{
26+
"src": "/assets/screenshot-wide.png",
27+
"sizes": "1280x720",
28+
"type": "image/png",
29+
"form_factor": "wide",
30+
"label": "EPUB Browser Desktop View"
31+
},
32+
{
33+
"src": "/assets/screenshot-narrow.png",
34+
"sizes": "750x1334",
35+
"type": "image/png",
36+
"form_factor": "narrow",
37+
"label": "EPUB Browser Mobile View"
2238
}
2339
],
2440
"categories": ["books", "education", "productivity"],
7.4 KB
Loading
5.47 KB
Loading

epub_browser/assets/sw.js

Lines changed: 25 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
const CACHE_NAME = 'epub-browser-v3';
1+
const CACHE_NAME = 'epub-browser-v5';
2+
23
const STATIC_ASSETS = [
34
'/',
45
'/index.html',
@@ -11,10 +12,14 @@ const STATIC_ASSETS = [
1112
'/assets/fa.all.min.css',
1213
'/assets/sortable.min.js',
1314
'/assets/medium-zoom.min.js',
14-
'/assets/manifest.json'
15+
'/assets/manifest.json',
16+
'/assets/icon-192.png',
17+
'/assets/icon-512.png',
18+
'/assets/screenshot-wide.png',
19+
'/assets/screenshot-narrow.png'
1520
];
1621

17-
// 安装事件 - 只缓存静态资源
22+
// 安装事件 - 预缓存静态资源
1823
self.addEventListener('install', (event) => {
1924
event.waitUntil(
2025
caches.open(CACHE_NAME)
@@ -46,22 +51,18 @@ self.addEventListener('activate', (event) => {
4651
self.clients.claim();
4752
});
4853

49-
// 判断是否应该缓存该请求
54+
// 判断是否应该缓存该请求(只缓存静态资源)
5055
function shouldCache(url) {
5156
const urlObj = new URL(url);
52-
53-
// 缓存书籍页面
54-
if (urlObj.pathname.startsWith('/book/')) {
55-
return true;
56-
}
57+
const pathname = urlObj.pathname;
5758

5859
// 缓存静态资源
59-
if (urlObj.pathname.startsWith('/assets/')) {
60+
if (pathname.startsWith('/assets/')) {
6061
return true;
6162
}
6263

63-
// 缓存根页面
64-
if (urlObj.pathname === '/' || urlObj.pathname === '/index.html') {
64+
// 只缓存根页面
65+
if (pathname === '/' || pathname === '/index.html') {
6566
return true;
6667
}
6768

@@ -80,29 +81,8 @@ self.addEventListener('fetch', (event) => {
8081
return;
8182
}
8283

83-
// 对于导航请求(页面跳转),使用网络优先策略
84+
// 导航请求(页面跳转)- 直接从网络获取,不经过 Service Worker
8485
if (event.request.mode === 'navigate') {
85-
event.respondWith(
86-
fetch(event.request, { redirect: 'follow' })
87-
.then((response) => {
88-
// 缓存响应(包括重定向后的最终响应)
89-
if (response) {
90-
const responseToCache = response.clone();
91-
caches.open(CACHE_NAME)
92-
.then((cache) => {
93-
cache.put(event.request, responseToCache);
94-
});
95-
}
96-
return response;
97-
})
98-
.catch(() => {
99-
// 网络失败时,返回缓存
100-
return caches.match(event.request)
101-
.then((cachedResponse) => {
102-
return cachedResponse || caches.match('/index.html');
103-
});
104-
})
105-
);
10686
return;
10787
}
10888

@@ -112,18 +92,19 @@ self.addEventListener('fetch', (event) => {
11292
.then((cachedResponse) => {
11393
// 如果有缓存,返回缓存
11494
if (cachedResponse) {
115-
// 同时在后台更新缓存
116-
if (shouldCache(event.request.url)) {
117-
fetchAndCache(event.request);
118-
}
11995
return cachedResponse;
12096
}
12197

12298
// 否则从网络获取
123-
return fetch(event.request, { redirect: 'follow' })
99+
return fetch(event.request)
124100
.then((response) => {
125-
// 缓存响应
126-
if (response && shouldCache(event.request.url)) {
101+
// 检查是否是有效的响应
102+
if (!response || response.status !== 200) {
103+
return response;
104+
}
105+
106+
// 如果应该缓存,则缓存响应
107+
if (shouldCache(event.request.url)) {
127108
const responseToCache = response.clone();
128109
caches.open(CACHE_NAME)
129110
.then((cache) => {
@@ -134,27 +115,11 @@ self.addEventListener('fetch', (event) => {
134115
return response;
135116
})
136117
.catch(() => {
137-
// 如果网络请求失败,尝试返回缓存的首页
138-
if (event.request.destination === 'document') {
139-
return caches.match('/index.html');
118+
// 如果网络请求失败,返回备用响应
119+
if (event.request.destination === 'image') {
120+
return new Response('', { status: 404 });
140121
}
141122
});
142123
})
143124
);
144125
});
145-
146-
// 后台更新缓存
147-
function fetchAndCache(request) {
148-
fetch(request, { redirect: 'follow' })
149-
.then((response) => {
150-
if (response) {
151-
caches.open(CACHE_NAME)
152-
.then((cache) => {
153-
cache.put(request, response);
154-
});
155-
}
156-
})
157-
.catch(() => {
158-
// 忽略错误
159-
});
160-
}

0 commit comments

Comments
 (0)