{{ .Title }}
++ {{- T "pageOffline" -}} +
+ {{ T `home` }} + {{ T `back` }} +diff --git a/assets/css/_page/_404.scss b/assets/css/_page/_404.scss index bd7a0f07..67a337b5 100644 --- a/assets/css/_page/_404.scss +++ b/assets/css/_page/_404.scss @@ -12,6 +12,7 @@ font-weight: 900; color: $global-font-color; letter-spacing: 1rem; + text-transform: uppercase; margin-bottom: 5rem; [theme=dark] & { color: $global-font-color-dark; diff --git a/assets/sw.js b/assets/sw.js new file mode 100644 index 00000000..9834365e --- /dev/null +++ b/assets/sw.js @@ -0,0 +1,244 @@ +const CACHE_VERSION = new URL(location).searchParams.get("version"); +const BASE_CACHE_FILES = ["/css/home.min.css", "/css/page.min.css", "/js/themes.min.js", "/site.webmanifest", "/404.html"]; +const OFFLINE_CACHE_FILES = ["/css/page.min.css", "/js/themes.min.js", "/offline/index.html"]; +const NOT_FOUND_CACHE_FILES = ["/css/page.min.css", "/js/themes.min.js", "/404.html"]; +const OFFLINE_PAGE = "/offline/index.html"; +const NOT_FOUND_PAGE = "/404.html"; + +const CACHE_VERSIONS = { + assets: "assets-v" + CACHE_VERSION, + content: "content-v" + CACHE_VERSION, + offline: "offline-v" + CACHE_VERSION, + notFound: "404-v" + CACHE_VERSION, +}; + +const MAX_TTL = { + "/": 3600, + html: 3600, + json: 86400, + js: 86400, + css: 86400, +}; + +const CACHE_BLACKLIST = [ + (str) => { + return !str.startsWith("http://localhost"); + }, +]; + +const SUPPORTED_METHODS = ["GET"]; + +function isBlacklisted(url) { + return CACHE_BLACKLIST.length > 0 + ? !CACHE_BLACKLIST.filter((rule) => { + if (typeof rule === "function") { + return !rule(url); + } else { + return false; + } + }).length + : false; +} + +function getFileExtension(url) { + let extension = url.split(".").reverse()[0].split("?")[0]; + return extension.endsWith("/") ? "/" : extension; +} + +function getTTL(url) { + if (typeof url === "string") { + let extension = getFileExtension(url); + if (typeof MAX_TTL[extension] === "number") { + return MAX_TTL[extension]; + } else { + return null; + } + } else { + return null; + } +} + +function installServiceWorker() { + return Promise.all([ + caches.open(CACHE_VERSIONS.assets).then((cache) => { + return cache.addAll(BASE_CACHE_FILES); + }), + caches.open(CACHE_VERSIONS.offline).then((cache) => { + return cache.addAll(OFFLINE_CACHE_FILES); + }), + caches.open(CACHE_VERSIONS.notFound).then((cache) => { + return cache.addAll(NOT_FOUND_CACHE_FILES); + }), + ]).then(() => { + return self.skipWaiting(); + }); +} + +function cleanupLegacyCache() { + let currentCaches = Object.keys(CACHE_VERSIONS).map((key) => { + return CACHE_VERSIONS[key]; + }); + + return new Promise((resolve, reject) => { + caches + .keys() + .then((keys) => { + return (legacyKeys = keys.filter((key) => { + return !~currentCaches.indexOf(key); + })); + }) + .then((legacy) => { + if (legacy.length) { + Promise.all( + legacy.map((legacyKey) => { + return caches.delete(legacyKey); + }) + ) + .then(() => { + resolve(); + }) + .catch((err) => { + reject(err); + }); + } else { + resolve(); + } + }) + .catch(() => { + reject(); + }); + }); +} + +function precacheUrl(url) { + if (!isBlacklisted(url)) { + caches.open(CACHE_VERSIONS.content).then((cache) => { + cache + .match(url) + .then((response) => { + if (!response) { + return fetch(url); + } else { + return null; + } + }) + .then((response) => { + if (response) { + return cache.put(url, response.clone()); + } else { + return null; + } + }); + }); + } +} + +self.addEventListener("install", (event) => { + event.waitUntil(Promise.all([installServiceWorker(), self.skipWaiting()])); +}); + +self.addEventListener("activate", (event) => { + event.waitUntil( + Promise.all([cleanupLegacyCache(), self.clients.claim(), self.skipWaiting()]).catch((err) => { + event.skipWaiting(); + }) + ); +}); + +self.addEventListener("fetch", (event) => { + event.respondWith( + caches.open(CACHE_VERSIONS.content).then((cache) => { + return cache + .match(event.request) + .then((response) => { + if (response) { + let headers = response.headers.entries(); + let date = null; + + for (let pair of headers) { + if (pair[0] === "date") { + date = new Date(pair[1]); + } + } + + if (date) { + let age = parseInt((new Date().getTime() - date.getTime()) / 1000); + let ttl = getTTL(event.request.url); + + if (ttl && age > ttl) { + return new Promise((resolve) => { + return fetch(event.request.clone()) + .then((updatedResponse) => { + if (updatedResponse) { + cache.put(event.request, updatedResponse.clone()); + resolve(updatedResponse); + } else { + resolve(response); + } + }) + .catch(() => { + resolve(response); + }); + }).catch((err) => { + return response; + }); + } else { + return response; + } + } else { + return response; + } + } else { + return null; + } + }) + .then((response) => { + if (response) { + return response; + } else { + return fetch(event.request.clone()) + .then((response) => { + if (response.status < 400) { + if (~SUPPORTED_METHODS.indexOf(event.request.method) && !isBlacklisted(event.request.url)) { + cache.put(event.request, response.clone()); + } + return response; + } else { + return caches.open(CACHE_VERSIONS.notFound).then((cache) => { + return cache.match(NOT_FOUND_PAGE); + }); + } + }) + .then((response) => { + if (response) { + return response; + } + }) + .catch(() => { + return caches.open(CACHE_VERSIONS.offline).then((offlineCache) => { + return offlineCache.match(OFFLINE_PAGE); + }); + }); + } + }) + .catch((error) => { + console.error(" Error in fetch handler:", error); + throw error; + }); + }) + ); +}); + +self.addEventListener("message", (event) => { + if (typeof event.data === "object" && typeof event.data.action === "string") { + switch (event.data.action) { + case "cache": + precacheUrl(event.data.url); + break; + default: + console.log("Unknown action: " + event.data.action); + break; + } + } +}); + diff --git a/content/offline.md b/content/offline.md new file mode 100644 index 00000000..93869d8f --- /dev/null +++ b/content/offline.md @@ -0,0 +1,5 @@ +--- +title: "Offline" +draft: false +offline: true +--- diff --git a/exampleSite/config.toml b/exampleSite/config.toml index d20cd49f..018ecb6f 100644 --- a/exampleSite/config.toml +++ b/exampleSite/config.toml @@ -446,6 +446,13 @@ ignoreErrors = ["err-missing-comment-accesstoken", "err-missing-oembed-accesstok # whether to enable CSS and JS source mapping SourceMap = false + # PWA config + [params.pwa] + # whether to enable PWA support + enable = true + # service-worker version + version = "1.0.0" + # Header config # 页面头部导航栏配置 [params.header] diff --git a/exampleSite/static/site.webmanifest b/exampleSite/static/site.webmanifest index 8fdc62c7..2e103083 100644 --- a/exampleSite/static/site.webmanifest +++ b/exampleSite/static/site.webmanifest @@ -5,7 +5,8 @@ { "src": "/android-chrome-192x192.png", "sizes": "192x192", - "type": "image/png" + "type": "image/png", + "purpose": "any maskable" }, { "src": "/android-chrome-512x512.png", @@ -13,7 +14,9 @@ "type": "image/png" } ], - "theme_color": "#ffffff", - "background_color": "#ffffff", - "display": "standalone" + "start_url": "/", + "display": "standalone", + "orientation" : "portrait", + "background_color": "#FFFFFF", + "theme_color": "#FFFFFF" } diff --git a/i18n/de.toml b/i18n/de.toml index eb56d151..ccb63dc6 100644 --- a/i18n/de.toml +++ b/i18n/de.toml @@ -149,6 +149,11 @@ other = "Seite nicht gefunden" other = "Leider konnte die von Ihnen angeforderte Seite nicht aufgerufen werden." # === 404.html === +# === Offline Page === +[pageOffline] +other = "Sie sind offline. Prüfe deine Internetverbindung." +# === Offline Page === + # === shortcodes/admonition.html === [note] other = "Notiz" diff --git a/i18n/en.toml b/i18n/en.toml index 87565e87..97bffe3b 100644 --- a/i18n/en.toml +++ b/i18n/en.toml @@ -145,6 +145,11 @@ other = "Page not found" other = "The page you're looking for doesn't exist. Sorry." # === 404.html === +# === Offline Page === +[pageOffline] +other = "You're offline. Check your internet connection." +# === Offline Page === + # === shortcodes/admonition.html === [note] other = "Note" diff --git a/i18n/es.toml b/i18n/es.toml index 7db11ff8..4cd2a6d2 100644 --- a/i18n/es.toml +++ b/i18n/es.toml @@ -149,6 +149,11 @@ other = "Página no encontrada" other = "La página que estás buscando no existe. Lo siento." # === 404.html === +# === Offline Page === +[pageOffline] +other = "Estás desconectado. Comprueba tu conexión a Internet." +# === Offline Page === + # === shortcodes/admonition.html === [note] other = "Nota" diff --git a/i18n/fr.toml b/i18n/fr.toml index ed74a624..09f6c5e3 100644 --- a/i18n/fr.toml +++ b/i18n/fr.toml @@ -149,6 +149,11 @@ other = "Page non trouvée" other = "Désolé, la page recherchée n'existe pas." # === 404.html === +# === Offline Page === +[pageOffline] +other = "Tu es hors ligne. Vérifiez votre connection internet." +# === Offline Page === + # === shortcodes/admonition.html === [note] other = "Remarque" diff --git a/i18n/id.toml b/i18n/id.toml index e93348f2..36f467ea 100644 --- a/i18n/id.toml +++ b/i18n/id.toml @@ -148,6 +148,11 @@ other = "Halaman tidak ditemukan" other = "Maaf, halaman yang Anda cari tidak ada." # === 404.html === +# === Offline Page === +[pageOffline] +other = "Anda sedang offline. Periksa koneksi internet Anda." +# === Offline Page === + # === shortcodes/admonition.html === [note] other = "Catatan" diff --git a/i18n/it.toml b/i18n/it.toml index a3d99d62..9f38bf12 100644 --- a/i18n/it.toml +++ b/i18n/it.toml @@ -149,6 +149,11 @@ other = "Pagina non trovata" other = "Mi spiace, la pagina cercata non esiste." # === 404.html === +# === Offline Page === +[pageOffline] +other = "Sei offline. Controlla la tua connessione Internet." +# === Offline Page === + # === shortcodes/admonition.html === [note] other = "Note" diff --git a/i18n/pl.toml b/i18n/pl.toml index b33f6eaf..ecaea511 100644 --- a/i18n/pl.toml +++ b/i18n/pl.toml @@ -146,6 +146,11 @@ other = "Nie znaleziono strony" other = "Wybacz, chyba coś namieszaliśmy." # === 404.html === +# === Offline Page === +[pageOffline] +other = "Jesteś offline. Sprawdź swoje łącze internetowe." +# === Offline Page === + # === shortcodes/admonition.html === [note] other = "Notka" diff --git a/i18n/pt-BR.toml b/i18n/pt-BR.toml index 11b7a7df..2d4e0fa9 100644 --- a/i18n/pt-BR.toml +++ b/i18n/pt-BR.toml @@ -149,6 +149,11 @@ other = "Página não encontrada" other = "A página que você procura não existe. Desculpe" # === 404.html === +# === Offline Page === +[pageOffline] +other = "Você está offline. Verifique sua conexão com a internet." +# === Offline Page === + # === shortcodes/admonition.html === [note] other = "Nota" diff --git a/i18n/ro.toml b/i18n/ro.toml index 049c023f..7348bfe7 100644 --- a/i18n/ro.toml +++ b/i18n/ro.toml @@ -149,6 +149,11 @@ other = "Pagina nu a fost găsită" other = "Pagina pe care o căutați nu există. Ne cerem scuze." # === 404.html === +# === Offline Page === +[pageOffline] +other = "Ești offline. Verificați conexiunea la internet." +# === Offline Page === + # === shortcodes/admonition.html === [note] other = "Notă" diff --git a/i18n/ru.toml b/i18n/ru.toml index 72dde963..dcf6ace4 100644 --- a/i18n/ru.toml +++ b/i18n/ru.toml @@ -149,6 +149,11 @@ other = "Страница не найдена" other = "Страница, которую вы ищете, не существует. Приносим извинения." # === 404.html === +# === Offline Page === +[pageOffline] +other = "Ты не в сети. Проверьте ваше интернет-соединение." +# === Offline Page === + # === shortcodes/admonition.html === [note] other = "Замечание" diff --git a/i18n/sr.toml b/i18n/sr.toml index 2f0a4022..63520277 100644 --- a/i18n/sr.toml +++ b/i18n/sr.toml @@ -146,6 +146,11 @@ other = "Страница није пронађена" other = "Страница коју тражите не постоји. Жао нам је." # === 404.html === +# === Offline Page === +[pageOffline] +other = "Ван мреже сте. Проверите интернет везу." +# === Offline Page === + # === shortcodes/admonition.html === [note] other = "Напомена" diff --git a/i18n/vi.toml b/i18n/vi.toml index 3d32eab2..d6b36384 100644 --- a/i18n/vi.toml +++ b/i18n/vi.toml @@ -148,6 +148,11 @@ other = "Không tìm thấy trang" other = "Trang bạn đang tìm kiếm không tồn tại. Xin lỗi." # === 404.html === +# === Offline Page === +[pageOffline] +other = "Bạn đang ngoại tuyến. Kiểm tra kết nối Internet của bạn." +# === Offline Page === + # === shortcodes/admonition.html === [note] other = "Ghi chú" diff --git a/i18n/zh-CN.toml b/i18n/zh-CN.toml index 7b987d01..377793b5 100644 --- a/i18n/zh-CN.toml +++ b/i18n/zh-CN.toml @@ -150,6 +150,11 @@ other = "页面没找到" other = "抱歉,您要查找的页面不存在。" # === 404.html === +# === Offline Page === +[pageOffline] +other = "你离线了。 检查您的互联网连接。" +# === Offline Page === + # === shortcodes/admonition.html === [note] other = "注意" diff --git a/layouts/_default/baseof.html b/layouts/_default/baseof.html index f3bdbcbe..68b9af6f 100644 --- a/layouts/_default/baseof.html +++ b/layouts/_default/baseof.html @@ -34,7 +34,14 @@ {{- block "content" . }}{{ end -}} +