From 2ae99faca92f6dfb7e9c64fc1d18160617b8980a Mon Sep 17 00:00:00 2001 From: hamid zarghami Date: Sat, 6 Jun 2026 10:04:47 +0330 Subject: [PATCH] once load splash --- next.config.ts | 15 ++++++- public/manifest.json | 16 ++++++- public/sw.js | 12 ++++- src/app/[name]/api/logo/route.ts | 3 +- src/app/[name]/layout.tsx | 15 ++++--- src/app/[name]/manifest.webmanifest/route.ts | 35 ++------------- src/app/api/pwa-icon/[size]/route.ts | 34 ++++++++++++++ src/app/layout.tsx | 3 +- src/components/PwaServiceWorker.tsx | 15 +++++++ src/components/menu/SideMenu.tsx | 36 ++++++++++----- src/components/wrapper/PreferenceWrapper.tsx | 47 ++++++++++++++++---- src/lib/helpers/pwaIcons.ts | 34 ++++++++++++++ 12 files changed, 201 insertions(+), 64 deletions(-) create mode 100644 src/app/api/pwa-icon/[size]/route.ts create mode 100644 src/components/PwaServiceWorker.tsx create mode 100644 src/lib/helpers/pwaIcons.ts diff --git a/next.config.ts b/next.config.ts index 3fac736..2f409ce 100644 --- a/next.config.ts +++ b/next.config.ts @@ -17,7 +17,20 @@ const nextConfig: NextConfig = { }, headers: async () => [ { - source: "/:path*", + source: "/sw.js", + headers: [ + { + key: "Cache-Control", + value: "public, max-age=0, must-revalidate", + }, + { + key: "Service-Worker-Allowed", + value: "/", + }, + ], + }, + { + source: "/((?!sw\\.js|api/pwa-icon).*)", headers: [ { key: "Cache-Control", diff --git a/public/manifest.json b/public/manifest.json index dab3e71..a630838 100644 --- a/public/manifest.json +++ b/public/manifest.json @@ -23,16 +23,28 @@ }, "icons": [ { - "src": "/icons/web-app-manifest-192x192.png", + "src": "/api/pwa-icon/192", "type": "image/png", "sizes": "192x192", "purpose": "any" }, { - "src": "/icons/web-app-manifest-512x512.png", + "src": "/api/pwa-icon/512", "type": "image/png", "sizes": "512x512", "purpose": "any" + }, + { + "src": "/api/pwa-icon/192", + "type": "image/png", + "sizes": "192x192", + "purpose": "maskable" + }, + { + "src": "/api/pwa-icon/512", + "type": "image/png", + "sizes": "512x512", + "purpose": "maskable" } ], "categories": [ diff --git a/public/sw.js b/public/sw.js index fc20ddc..8574449 100644 --- a/public/sw.js +++ b/public/sw.js @@ -1 +1,11 @@ -if(!self.define){let e,s={};const a=(a,c)=>(a=new URL(a+".js",c).href,s[a]||new Promise(s=>{if("document"in self){const e=document.createElement("script");e.src=a,e.onload=s,document.head.appendChild(e)}else e=a,importScripts(a),s()}).then(()=>{let e=s[a];if(!e)throw new Error(`Module ${a} didn’t register its module`);return e}));self.define=(c,r)=>{const i=e||("document"in self?document.currentScript.src:"")||location.href;if(s[i])return;let t={};const n=e=>a(e,i),b={module:{uri:i},exports:t,require:n};s[i]=Promise.all(c.map(e=>b[e]||n(e))).then(e=>(r(...e),t))}}define(["./workbox-4754cb34"],function(e){"use strict";importScripts(),self.skipWaiting(),e.clientsClaim(),e.precacheAndRoute([{url:"/_favicon.ico",revision:"28202080f3527f526a24b1119885536e"},{url:"/_next/app-build-manifest.json",revision:"b4dc5df94e17fe4b049a346310480715"},{url:"/_next/static/chunks/1135-3ed61c8919ef5fa0.js",revision:"3ed61c8919ef5fa0"},{url:"/_next/static/chunks/1255-8befde0980f5cba9.js",revision:"8befde0980f5cba9"},{url:"/_next/static/chunks/1524-7fb8905e0dfea939.js",revision:"7fb8905e0dfea939"},{url:"/_next/static/chunks/1670.f8712386b272b9c7.js",revision:"f8712386b272b9c7"},{url:"/_next/static/chunks/1733-94b29f2478acfe12.js",revision:"94b29f2478acfe12"},{url:"/_next/static/chunks/1767-e519eb578d6fcc2c.js",revision:"e519eb578d6fcc2c"},{url:"/_next/static/chunks/2113.1e83138010f4f194.js",revision:"1e83138010f4f194"},{url:"/_next/static/chunks/2454-de2ed7987d77521f.js",revision:"de2ed7987d77521f"},{url:"/_next/static/chunks/2476-e7648a2df382d65d.js",revision:"e7648a2df382d65d"},{url:"/_next/static/chunks/2551-6156c8c9dbc093cd.js",revision:"6156c8c9dbc093cd"},{url:"/_next/static/chunks/2617.828c2ea11fe1e484.js",revision:"828c2ea11fe1e484"},{url:"/_next/static/chunks/2619-b8db57ac19da49ac.js",revision:"b8db57ac19da49ac"},{url:"/_next/static/chunks/2769-4aff7270014224d1.js",revision:"4aff7270014224d1"},{url:"/_next/static/chunks/3544-aeaf44b8aa6f0017.js",revision:"aeaf44b8aa6f0017"},{url:"/_next/static/chunks/3686.ffaedffe72336ebb.js",revision:"ffaedffe72336ebb"},{url:"/_next/static/chunks/4022-c6095792365b51bc.js",revision:"c6095792365b51bc"},{url:"/_next/static/chunks/4086-85ae830d8ef183df.js",revision:"85ae830d8ef183df"},{url:"/_next/static/chunks/4161-a2b68b5bbc877d6a.js",revision:"a2b68b5bbc877d6a"},{url:"/_next/static/chunks/4557-0d4181d382558f3d.js",revision:"0d4181d382558f3d"},{url:"/_next/static/chunks/4909-1d47030dd4b75929.js",revision:"1d47030dd4b75929"},{url:"/_next/static/chunks/4bd1b696-100b9d70ed4e49c1.js",revision:"100b9d70ed4e49c1"},{url:"/_next/static/chunks/5070.ad19d2678fe10023.js",revision:"ad19d2678fe10023"},{url:"/_next/static/chunks/5125-a51407a3721216f6.js",revision:"a51407a3721216f6"},{url:"/_next/static/chunks/5239-b5d471afce036544.js",revision:"b5d471afce036544"},{url:"/_next/static/chunks/5511-8b8de44a17ff4403.js",revision:"8b8de44a17ff4403"},{url:"/_next/static/chunks/5579.fc7075d0bf0d6b82.js",revision:"fc7075d0bf0d6b82"},{url:"/_next/static/chunks/643-b54442cd82702653.js",revision:"b54442cd82702653"},{url:"/_next/static/chunks/6430.be39915572b2f5bb.js",revision:"be39915572b2f5bb"},{url:"/_next/static/chunks/6784-ffba27fc8f8f47c4.js",revision:"ffba27fc8f8f47c4"},{url:"/_next/static/chunks/6914.a2221be2e59a3d08.js",revision:"a2221be2e59a3d08"},{url:"/_next/static/chunks/7149.c9a4fc0086336833.js",revision:"c9a4fc0086336833"},{url:"/_next/static/chunks/7173-e91971af81603133.js",revision:"e91971af81603133"},{url:"/_next/static/chunks/7286-9c0db7dec3319b8b.js",revision:"9c0db7dec3319b8b"},{url:"/_next/static/chunks/7493.e35264fc9797c5f4.js",revision:"e35264fc9797c5f4"},{url:"/_next/static/chunks/7678-280d9eda3fc7c98c.js",revision:"280d9eda3fc7c98c"},{url:"/_next/static/chunks/7840-af474069c8a3a3d7.js",revision:"af474069c8a3a3d7"},{url:"/_next/static/chunks/7930-da9fb80421f49fd6.js",revision:"da9fb80421f49fd6"},{url:"/_next/static/chunks/8016.fe6fba52773595d8.js",revision:"fe6fba52773595d8"},{url:"/_next/static/chunks/8126-f686efbf37ae2295.js",revision:"f686efbf37ae2295"},{url:"/_next/static/chunks/8268-80f9c3ec864a0098.js",revision:"80f9c3ec864a0098"},{url:"/_next/static/chunks/8398-c7a7fdff7a9f5996.js",revision:"c7a7fdff7a9f5996"},{url:"/_next/static/chunks/8595-277e564473879964.js",revision:"277e564473879964"},{url:"/_next/static/chunks/8722.a10f00e99dd3c329.js",revision:"a10f00e99dd3c329"},{url:"/_next/static/chunks/8837-522c3b8d42cb558a.js",revision:"522c3b8d42cb558a"},{url:"/_next/static/chunks/894-c306c4d87dba8bf9.js",revision:"c306c4d87dba8bf9"},{url:"/_next/static/chunks/9126-9e7d7f06515406cb.js",revision:"9e7d7f06515406cb"},{url:"/_next/static/chunks/9340-17206fc48fc23e47.js",revision:"17206fc48fc23e47"},{url:"/_next/static/chunks/9521-8cc8b53c709289ed.js",revision:"8cc8b53c709289ed"},{url:"/_next/static/chunks/9597-6bcfb1689c5518b4.js",revision:"6bcfb1689c5518b4"},{url:"/_next/static/chunks/9659-a921536c27172a58.js",revision:"a921536c27172a58"},{url:"/_next/static/chunks/9776-b1f281762e0715ee.js",revision:"b1f281762e0715ee"},{url:"/_next/static/chunks/9852-40884b861a14d4b9.js",revision:"40884b861a14d4b9"},{url:"/_next/static/chunks/9973-e620798030befbb5.js",revision:"e620798030befbb5"},{url:"/_next/static/chunks/9986-eecd5666098515b8.js",revision:"eecd5666098515b8"},{url:"/_next/static/chunks/app/%5Bname%5D/(Dialogs)/cart/page-e6b8cceed8c46821.js",revision:"e6b8cceed8c46821"},{url:"/_next/static/chunks/app/%5Bname%5D/(Dialogs)/game/2048/layout-4198e590338516dc.js",revision:"4198e590338516dc"},{url:"/_next/static/chunks/app/%5Bname%5D/(Dialogs)/game/2048/page-c0da15fea7b2724e.js",revision:"c0da15fea7b2724e"},{url:"/_next/static/chunks/app/%5Bname%5D/(Dialogs)/game/page-1b0749f4c6794162.js",revision:"1b0749f4c6794162"},{url:"/_next/static/chunks/app/%5Bname%5D/(Dialogs)/game/the-cube/page-06b7529e4a796b4c.js",revision:"06b7529e4a796b4c"},{url:"/_next/static/chunks/app/%5Bname%5D/(Dialogs)/game/tower-builder/layout-b8b838e408313a22.js",revision:"b8b838e408313a22"},{url:"/_next/static/chunks/app/%5Bname%5D/(Dialogs)/game/tower-builder/page-2e99b90d289b9ad1.js",revision:"2e99b90d289b9ad1"},{url:"/_next/static/chunks/app/%5Bname%5D/(Dialogs)/layout-376cbb1cf72e09ad.js",revision:"376cbb1cf72e09ad"},{url:"/_next/static/chunks/app/%5Bname%5D/(Dialogs)/notifications/page-fb3f72dccdf6d96f.js",revision:"fb3f72dccdf6d96f"},{url:"/_next/static/chunks/app/%5Bname%5D/(Dialogs)/order/checkout/%5Bid%5D/page-bb37bea9d3fd895a.js",revision:"bb37bea9d3fd895a"},{url:"/_next/static/chunks/app/%5Bname%5D/(Dialogs)/order/track/%5Bid%5D/page-e955edc0d7c021e3.js",revision:"e955edc0d7c021e3"},{url:"/_next/static/chunks/app/%5Bname%5D/(Dialogs)/pager/page-bf2f4e82ba34d2d3.js",revision:"bf2f4e82ba34d2d3"},{url:"/_next/static/chunks/app/%5Bname%5D/(Dialogs)/report/page-661e3480efc4737b.js",revision:"661e3480efc4737b"},{url:"/_next/static/chunks/app/%5Bname%5D/(Dialogs)/verify/%5Bid%5D/page-b84dc231c6d98745.js",revision:"b84dc231c6d98745"},{url:"/_next/static/chunks/app/%5Bname%5D/(Main)/%5Bid%5D/page-206629b73ccd402c.js",revision:"206629b73ccd402c"},{url:"/_next/static/chunks/app/%5Bname%5D/(Main)/about/layout-4643059fef32f370.js",revision:"4643059fef32f370"},{url:"/_next/static/chunks/app/%5Bname%5D/(Main)/about/page-88ea2d564b2f6845.js",revision:"88ea2d564b2f6845"},{url:"/_next/static/chunks/app/%5Bname%5D/(Main)/chat/%5Bid%5D/page-139f2080eabb8174.js",revision:"139f2080eabb8174"},{url:"/_next/static/chunks/app/%5Bname%5D/(Main)/chat/nearby/page-e79e439de8581484.js",revision:"e79e439de8581484"},{url:"/_next/static/chunks/app/%5Bname%5D/(Main)/chat/page-d85cc1cc3b9a90a7.js",revision:"d85cc1cc3b9a90a7"},{url:"/_next/static/chunks/app/%5Bname%5D/(Main)/layout-8137cde178af17fe.js",revision:"8137cde178af17fe"},{url:"/_next/static/chunks/app/%5Bname%5D/(Main)/loading-5d7f7eb7a6350d78.js",revision:"5d7f7eb7a6350d78"},{url:"/_next/static/chunks/app/%5Bname%5D/(Main)/order/history/layout-4643059fef32f370.js",revision:"4643059fef32f370"},{url:"/_next/static/chunks/app/%5Bname%5D/(Main)/order/history/page-37d6bd9c86b20deb.js",revision:"37d6bd9c86b20deb"},{url:"/_next/static/chunks/app/%5Bname%5D/(Main)/order/rate/%5BorderId%5D/page-016bc511d3b3c0bd.js",revision:"016bc511d3b3c0bd"},{url:"/_next/static/chunks/app/%5Bname%5D/(Main)/page-7bacd2cddb53a66a.js",revision:"7bacd2cddb53a66a"},{url:"/_next/static/chunks/app/%5Bname%5D/(Main)/transactions/discount/club/page-f26fbefb862c2a0a.js",revision:"f26fbefb862c2a0a"},{url:"/_next/static/chunks/app/%5Bname%5D/(Main)/transactions/discount/page-cbe32da10d307509.js",revision:"cbe32da10d307509"},{url:"/_next/static/chunks/app/%5Bname%5D/(Main)/transactions/page-128b87383357c369.js",revision:"128b87383357c369"},{url:"/_next/static/chunks/app/%5Bname%5D/(Profile)/favorite/page-5aa7ac6077807cbe.js",revision:"5aa7ac6077807cbe"},{url:"/_next/static/chunks/app/%5Bname%5D/(Profile)/layout-c4fff972eb63ad48.js",revision:"c4fff972eb63ad48"},{url:"/_next/static/chunks/app/%5Bname%5D/(Profile)/profile/address/new/page-7627f7d7bd224182.js",revision:"7627f7d7bd224182"},{url:"/_next/static/chunks/app/%5Bname%5D/(Profile)/profile/address/page-27f9fd5e25a6b1a7.js",revision:"27f9fd5e25a6b1a7"},{url:"/_next/static/chunks/app/%5Bname%5D/(Profile)/profile/edit/page-b302cfc4f1a3be58.js",revision:"b302cfc4f1a3be58"},{url:"/_next/static/chunks/app/%5Bname%5D/(Profile)/profile/page-9cad198b9b595b1d.js",revision:"9cad198b9b595b1d"},{url:"/_next/static/chunks/app/%5Bname%5D/(Profile)/profile/settings/page-5b8fcd1c943f30b1.js",revision:"5b8fcd1c943f30b1"},{url:"/_next/static/chunks/app/%5Bname%5D/(Profile)/profile/wallet/page-e9ce702c91288abd.js",revision:"e9ce702c91288abd"},{url:"/_next/static/chunks/app/%5Bname%5D/(Proxy)/auth/layout-4643059fef32f370.js",revision:"4643059fef32f370"},{url:"/_next/static/chunks/app/%5Bname%5D/(Proxy)/auth/login/page-87c2a04f6a6c4b44.js",revision:"87c2a04f6a6c4b44"},{url:"/_next/static/chunks/app/%5Bname%5D/(Proxy)/auth/page-192221cf38062f0d.js",revision:"192221cf38062f0d"},{url:"/_next/static/chunks/app/%5Bname%5D/(Proxy)/auth/reset-password/page-f082f0965e2ff594.js",revision:"f082f0965e2ff594"},{url:"/_next/static/chunks/app/%5Bname%5D/(Proxy)/auth/signup/page-c0c7e7e01d16e0a5.js",revision:"c0c7e7e01d16e0a5"},{url:"/_next/static/chunks/app/%5Bname%5D/layout-077e763ac69b7d5a.js",revision:"077e763ac69b7d5a"},{url:"/_next/static/chunks/app/%5Bname%5D/manifest.webmanifest/route-4643059fef32f370.js",revision:"4643059fef32f370"},{url:"/_next/static/chunks/app/_not-found/page-034f1789b0eaeb6b.js",revision:"034f1789b0eaeb6b"},{url:"/_next/static/chunks/app/auth/layout-4643059fef32f370.js",revision:"4643059fef32f370"},{url:"/_next/static/chunks/app/auth/login/page-38f4f39362b8dbb8.js",revision:"38f4f39362b8dbb8"},{url:"/_next/static/chunks/app/auth/page-3b2fcdc559f5bfe6.js",revision:"3b2fcdc559f5bfe6"},{url:"/_next/static/chunks/app/auth/reset-password/page-699a25871ef237ec.js",revision:"699a25871ef237ec"},{url:"/_next/static/chunks/app/auth/signup/page-b07e21e5a2f7cfeb.js",revision:"b07e21e5a2f7cfeb"},{url:"/_next/static/chunks/app/layout-de5de0a10cc5bac7.js",revision:"de5de0a10cc5bac7"},{url:"/_next/static/chunks/app/loading-5d7f7eb7a6350d78.js",revision:"5d7f7eb7a6350d78"},{url:"/_next/static/chunks/app/page-99f323bd08352c38.js",revision:"99f323bd08352c38"},{url:"/_next/static/chunks/d0deef33.59194872e4111dca.js",revision:"59194872e4111dca"},{url:"/_next/static/chunks/framework-ce757b396f77691a.js",revision:"ce757b396f77691a"},{url:"/_next/static/chunks/main-app-fb228aef04f005ac.js",revision:"fb228aef04f005ac"},{url:"/_next/static/chunks/main-b0d19be3b3688f95.js",revision:"b0d19be3b3688f95"},{url:"/_next/static/chunks/pages/_app-4b3fb5e477a0267f.js",revision:"4b3fb5e477a0267f"},{url:"/_next/static/chunks/pages/_error-c970d8b55ace1b48.js",revision:"c970d8b55ace1b48"},{url:"/_next/static/chunks/polyfills-42372ed130431b0a.js",revision:"846118c33b2c0e922d7b3a7676f81f6f"},{url:"/_next/static/chunks/webpack-c6a8ceb41b7fc9ee.js",revision:"c6a8ceb41b7fc9ee"},{url:"/_next/static/css/2c2be3c8248b4741.css",revision:"2c2be3c8248b4741"},{url:"/_next/static/css/54a748dd3edce6c4.css",revision:"54a748dd3edce6c4"},{url:"/_next/static/media/layers-2x.9859cd12.png",revision:"9859cd12"},{url:"/_next/static/media/layers.ef6db872.png",revision:"ef6db872"},{url:"/_next/static/media/marker-icon.d577052a.png",revision:"d577052a"},{url:"/_next/static/thqED-naZlwHus1J8APUF/_buildManifest.js",revision:"89d1edfa442c2e8f95dcf06d621e6ed7"},{url:"/_next/static/thqED-naZlwHus1J8APUF/_ssgManifest.js",revision:"b6652df95db52feb4daf4eca35380933"},{url:"/apple-icon.png",revision:"0c11163d9876662afd598cdb681bb2e4"},{url:"/assets/css/fonts.css",revision:"b23bd571553a35e52be7bdfb8805ba5e"},{url:"/assets/fonts/Irancell_Bold.ttf",revision:"e5160b0c919e044bfbcf18a58dd04ad2"},{url:"/assets/fonts/Irancell_Bold.woff",revision:"ed06d53485fd8044e79894bedc597104"},{url:"/assets/fonts/Irancell_Bold.woff2",revision:"04e0058cbeb4dfe083dbf98370ae885e"},{url:"/assets/fonts/Irancell_ExtraBold.ttf",revision:"aca587f4f429c26e887e102f4d46bd27"},{url:"/assets/fonts/Irancell_ExtraBold.woff",revision:"eb70794e10fd476987930ef523ed8a33"},{url:"/assets/fonts/Irancell_ExtraBold.woff2",revision:"5f66486a89926925c4eabcb76aea6ca1"},{url:"/assets/fonts/Irancell_ExtraLight.ttf",revision:"85dd3b644afbabbea860e70d91adb665"},{url:"/assets/fonts/Irancell_ExtraLight.woff",revision:"7491ed7a70e2b9f8fa19c20eac497c12"},{url:"/assets/fonts/Irancell_ExtraLight.woff2",revision:"807b703a3924972991de43d76764f404"},{url:"/assets/fonts/Irancell_Light.ttf",revision:"911a07e375667ba6feeca9a527bc537d"},{url:"/assets/fonts/Irancell_Light.woff",revision:"9c1e83ee95bb25d5adc97011bbab2ae5"},{url:"/assets/fonts/Irancell_Light.woff2",revision:"34404e8d01d1ee40a5bc00311ae2add4"},{url:"/assets/fonts/Irancell_Medium.ttf",revision:"2b8b7463718f1930afc053208efe986e"},{url:"/assets/fonts/Irancell_Medium.woff",revision:"e952c2a8c784e1b13c606f4b9b300310"},{url:"/assets/fonts/Irancell_Medium.woff2",revision:"1955af7b1d4244b4034b793c4435a7e3"},{url:"/assets/fonts/Irancell_Regular.ttf",revision:"3c29a31eaec7ddd32d41879dfcce95b8"},{url:"/assets/fonts/Irancell_Regular.woff",revision:"c9d7d00bddfd0595b68e750574b28312"},{url:"/assets/fonts/Irancell_Regular.woff2",revision:"89eb5fb2d1409229407d1a75426acda0"},{url:"/assets/images/2048_logo.png",revision:"b91d3acc79bde15f69f5bc21ef338002"},{url:"/assets/images/avatar.svg",revision:"ce25555161d92f081746041e2815919a"},{url:"/assets/images/controller.png",revision:"ba35bede4bc04c309c37c14974530474"},{url:"/assets/images/cube_logo.jpeg",revision:"8e61d8d9bfbf30d4b2da3069394f7a5d"},{url:"/assets/images/danak-logo.png",revision:"bfbbcd927b3e359c7ab8047211f7c3a3"},{url:"/assets/images/fast-delivery.svg",revision:"be90d540ca04e6fdaa3547da527242b6"},{url:"/assets/images/food-image.png",revision:"34520f66d5224a61eca7e83646bdb31e"},{url:"/assets/images/food-preview-hq.png",revision:"42968e21a3044f80db7399459768a16c"},{url:"/assets/images/food-preview.png",revision:"bfc910395c189bfc145fa950dbcca749"},{url:"/assets/images/login-banner.png",revision:"29a467b532dbf3c6e8bcd45e08ea3cde"},{url:"/assets/images/report-hero.svg",revision:"fe10be00fde304933f91cdedab597494"},{url:"/assets/images/tower_building_logo.webp",revision:"1ed5367e06a86ade6bfcd31a186667d4"},{url:"/assets/images/user-avatar.png",revision:"0d4b4612ad00442263d2a41754fcadcc"},{url:"/file.svg",revision:"d09f95206c3fa0bb9bd9fefabfd0ea71"},{url:"/game2048/index.html",revision:"6b93238ee2b19bbcec36a1cb7f946d8f"},{url:"/game2048/js/animframe_polyfill.js",revision:"7301b5c1e78a2211048c0f6a74215616"},{url:"/game2048/js/application.js",revision:"761f76a3fb1219922d05cda85a734a0c"},{url:"/game2048/js/bind_polyfill.js",revision:"dc3ec09e5eccbaa071a6d71a15d3fc52"},{url:"/game2048/js/classlist_polyfill.js",revision:"e565744097576fa604948c29012c7a02"},{url:"/game2048/js/game_manager.js",revision:"5ce37a1b0a3cf89ff6d671477d5427e6"},{url:"/game2048/js/grid.js",revision:"29c4bd95cc49bc1b0acde5543748e9c2"},{url:"/game2048/js/html_actuator.js",revision:"921c7366eaedd6e4a9082b316c136065"},{url:"/game2048/js/keyboard_input_manager.js",revision:"5259e6366694968b81e19cc3df4ca4af"},{url:"/game2048/js/local_storage_manager.js",revision:"606e425b8cfe569012272377b04c3f5c"},{url:"/game2048/js/tile.js",revision:"581495efb73cfea279196387d77c83e3"},{url:"/game2048/style/main.css",revision:"a33923627a6bffbceeec168ca31a866a"},{url:"/globe.svg",revision:"2aaafa6a49b6563925fe440891e32717"},{url:"/icon0.svg",revision:"6d2cbb302b9f3ef7f2b43ae7d355f6c9"},{url:"/icon1.png",revision:"14e00a23129ccbbe02a835997ecca6c0"},{url:"/icons/restaurant-marker.svg",revision:"72add246cf474e9b703edd352a50b1c8"},{url:"/icons/web-app-manifest-192x192.png",revision:"2bd31374f516557201d060ad4c5695c4"},{url:"/icons/web-app-manifest-512x512.png",revision:"d0f9aa93e12fc2ed5b220efa3db4e689"},{url:"/manifest.json",revision:"fcaa8d06b59646efa18f5d20d22b5ef9"},{url:"/manifests/zhivan/manifest.json",revision:"d4005805de3b85c31998a3fb4bb1dad3"},{url:"/marker-icon-2x.png",revision:"1c824216f354218b04b25a57e0f7ab1f"},{url:"/marker-icon.png",revision:"1c824216f354218b04b25a57e0f7ab1f"},{url:"/marker-shadow.png",revision:"44014eaa598e7050354603adfb44f812"},{url:"/next.svg",revision:"8e061864f388b47f33a1c3780831193e"},{url:"/the-cube-master/Cube.sublime-project",revision:"c9ede49bd4155724553b3e47c9f5d31c"},{url:"/the-cube-master/assets/css/styles.css",revision:"7402e26863e9fb70f6bd4162fd1857d9"},{url:"/the-cube-master/assets/icons/android-chrome-192x192.png",revision:"8ffe7a995dbeee391422de4288db1bbd"},{url:"/the-cube-master/assets/icons/android-chrome-512x512.png",revision:"b621d13863808ac7cfd0a81727115617"},{url:"/the-cube-master/assets/icons/apple-touch-icon.png",revision:"02b1f926e6bbddf44e610f9750537193"},{url:"/the-cube-master/assets/icons/browserconfig.xml",revision:"490efe304d7b8c82275b847fb4d6a8d9"},{url:"/the-cube-master/assets/icons/favicon-16x16.png",revision:"f37e7184f4911db7782f841e09cf2ac5"},{url:"/the-cube-master/assets/icons/favicon-32x32.png",revision:"48d2904ce77b9b4767e1bc1cf88ab899"},{url:"/the-cube-master/assets/icons/favicon.ico",revision:"fdde783ece79466cf26b731ea08c379a"},{url:"/the-cube-master/assets/icons/meta-image.png",revision:"632326ed76acd43c9bfacfe9288ec1c6"},{url:"/the-cube-master/assets/icons/mstile-150x150.png",revision:"380af42f7d461fdb68ed5a9bed69d512"},{url:"/the-cube-master/assets/icons/safari-pinned-tab.svg",revision:"0da2422bcb6158563c00513a716c5bdc"},{url:"/the-cube-master/assets/icons/site.webmanifest",revision:"f325a0ada80ea316d8581231f6915e2b"},{url:"/the-cube-master/assets/icons/twitter-card.png",revision:"83baacfbfa8921a947206ce051574e53"},{url:"/the-cube-master/assets/js/cube.js",revision:"7479eb36d667a93791403bd7006979e8"},{url:"/the-cube-master/assets/js/three.js",revision:"c692f22af631cc6caffc843babda40dc"},{url:"/the-cube-master/index.html",revision:"7575e0ee96ebe4c26afeb78aa90b49e3"},{url:"/the-cube-master/package-lock.json",revision:"5e940e2832178e5873e5d993b8e15d89"},{url:"/the-cube-master/package.json",revision:"0799d850e7741c672acfa42a2d39011e"},{url:"/the-cube-master/rollup.config.build.js",revision:"aef28e2d4ed1f2d890479fa9b15b4e28"},{url:"/the-cube-master/rollup.config.dev.js",revision:"6969fc235f1ed2fefeb400486bd49a2a"},{url:"/the-cube-master/src/js/Animation.js",revision:"55c816b9e3f730a3488c20a5db91b449"},{url:"/the-cube-master/src/js/Background.js",revision:"bbe4244cfc89e24129042b34da8dd1b6"},{url:"/the-cube-master/src/js/Confetti.js",revision:"4fcfe17b1f1be975997861835801fded"},{url:"/the-cube-master/src/js/Controls.js",revision:"ba7246b61cd147cc6ed952a39a54765e"},{url:"/the-cube-master/src/js/Cube.js",revision:"846ddd7d83539a82ea16185e5aa34c0f"},{url:"/the-cube-master/src/js/Draggable.js",revision:"a48fe7dbd75ae600be877d974c29c064"},{url:"/the-cube-master/src/js/Easing.js",revision:"3a94c67d3baab12058899c281cc1a01c"},{url:"/the-cube-master/src/js/Game.js",revision:"9cf3aaccb3e48c738b9df2bb8ae33238"},{url:"/the-cube-master/src/js/Icons.js",revision:"2e027e23ae62dfd59bbd9261a5d73f73"},{url:"/the-cube-master/src/js/Keyboard.js",revision:"b6825a3defe926d6342109a4a6bd6ee5"},{url:"/the-cube-master/src/js/KeyboardArrows.js",revision:"71ecda1e77666977094bbb8f2bdced14"},{url:"/the-cube-master/src/js/Preferences.js",revision:"eb50eff5cff9756814f657b0ce369ed5"},{url:"/the-cube-master/src/js/Range.js",revision:"a9e2123a2f3e0518bccb1fdbf444c622"},{url:"/the-cube-master/src/js/Scores.js",revision:"e0d4883d6a40f798ab9e78594b88fe14"},{url:"/the-cube-master/src/js/Scrambler.js",revision:"15a01d5ec40d03163fd10c09c7867032"},{url:"/the-cube-master/src/js/States.js",revision:"3ac9f3571a456ab832c823f4646e84e1"},{url:"/the-cube-master/src/js/Storage.js",revision:"5aedda4018118a71fe0345e8a9df1bee"},{url:"/the-cube-master/src/js/ThemeEditor.js",revision:"b37b86f31878ec36b26c4a6b9847d8b1"},{url:"/the-cube-master/src/js/Themes.js",revision:"6e56d1267397ec3ac798abbab3ccc90e"},{url:"/the-cube-master/src/js/Timer.js",revision:"e44762f850ad9caac8411e3a3c8c2b31"},{url:"/the-cube-master/src/js/Transition.js",revision:"a780092634ea446e640a35b66d632255"},{url:"/the-cube-master/src/js/Tween.js",revision:"57944ef9391f3ae32487772fb4f1af14"},{url:"/the-cube-master/src/js/World.js",revision:"b2afc2c33e70361136142ab681ce8e01"},{url:"/the-cube-master/src/js/plugins/RoundedBoxGeometry.js",revision:"69908bd249203343b5cc29e04e187585"},{url:"/the-cube-master/src/js/plugins/RoundedPlaneGeometry.js",revision:"2288bf0c57b82eeabb8c487d218a7f7b"},{url:"/the-cube-master/src/scss/components/_btn.sass",revision:"68535665f730818d48faa10feab1d730"},{url:"/the-cube-master/src/scss/components/_document.sass",revision:"3b1328c28646a86fa1fab61a3ec93645"},{url:"/the-cube-master/src/scss/components/_icon.sass",revision:"41d2f8bf7473fea14073f43ba7c25d28"},{url:"/the-cube-master/src/scss/components/_range.sass",revision:"da6798df303f6288dd1eea4a47a79d23"},{url:"/the-cube-master/src/scss/components/_stats.sass",revision:"92c85bb13d2c1836ccd09ded650f0eea"},{url:"/the-cube-master/src/scss/components/_text.sass",revision:"0b46c8ad6d975a235b3179623558b547"},{url:"/the-cube-master/src/scss/components/_ui.sass",revision:"edd147b24c5e726dc777add05e0ca602"},{url:"/the-cube-master/src/scss/fonts/_bungee.scss",revision:"2b3ceaa183bd434c220598f8f481f23f"},{url:"/the-cube-master/src/scss/mixins/_inset.sass",revision:"80dc78000177e09a2729be72fbf45d1b"},{url:"/the-cube-master/src/scss/styles.sass",revision:"d52649bb27130a57d09dfc7412407f9b"},{url:"/the-cube-master/src/scss/variables/_easings.sass",revision:"6b4ab85eeaced5fb263cda720c8e7402"},{url:"/the-cube-master/upup.min.js",revision:"38fbecba3dda30f368cfc7540ba00a6f"},{url:"/the-cube-master/upup.sw.min.js",revision:"3b063b8cb57165f648f2d31c97267eac"},{url:"/tower_game-master/LICENSE",revision:"9f7f36ad1fce9e5020bf413d8729f341"},{url:"/tower_game-master/README.md",revision:"3c1e12d3f0090f0ddd8f35e987e03021"},{url:"/tower_game-master/README.zh-CN.md",revision:"2763768179add897ebec05b92cecf8b9"},{url:"/tower_game-master/assets/background.png",revision:"0591066822e3dc5a9fa192d6ff93febd"},{url:"/tower_game-master/assets/bgm.mp3",revision:"452631259961492dc242c9dc387029d4"},{url:"/tower_game-master/assets/bgm.ogg",revision:"3a9d380bc75819ea7d79c10078c35ead"},{url:"/tower_game-master/assets/block-perfect.png",revision:"8e8055c554e89dadbaddcaa654fbb27d"},{url:"/tower_game-master/assets/block-rope.png",revision:"b2d06affd35057fa98e5163cc1da127e"},{url:"/tower_game-master/assets/block.png",revision:"0ec448189771fa3853f996940ad50a4f"},{url:"/tower_game-master/assets/c1.png",revision:"a2a2c04637f828e594d8902e688584a4"},{url:"/tower_game-master/assets/c2.png",revision:"fe9c57c36bbe814f7cab1a3951165d1f"},{url:"/tower_game-master/assets/c3.png",revision:"0a1ec4117b476c4e87d7a95f9639d3cf"},{url:"/tower_game-master/assets/c4.png",revision:"e489d0d8ab26d31704efb422cab06505"},{url:"/tower_game-master/assets/c5.png",revision:"fd6730de6c4cdab9fbd193f09deaef15"},{url:"/tower_game-master/assets/c6.png",revision:"3a178eeaae835ee7fba7b32d2d21a0af"},{url:"/tower_game-master/assets/c7.png",revision:"669b88c1527505093898bab27586d249"},{url:"/tower_game-master/assets/c8.png",revision:"75f0935677ca855c6e6f43f53315de58"},{url:"/tower_game-master/assets/drop-perfect.mp3",revision:"6acf9a7179389374c1a364143eed031c"},{url:"/tower_game-master/assets/drop-perfect.ogg",revision:"815a7e81d4ddc3d39f8f253015a68683"},{url:"/tower_game-master/assets/drop.mp3",revision:"c5015d0ccc8addcbd5d2de27fd224587"},{url:"/tower_game-master/assets/drop.ogg",revision:"78ee7bdda32a787a8221ff6f02d6654e"},{url:"/tower_game-master/assets/f1.png",revision:"20a96ff1bb411560f5c6cefc814c662d"},{url:"/tower_game-master/assets/f2.png",revision:"d24bd5bba0c8eb0a0180b1af22b4249b"},{url:"/tower_game-master/assets/f3.png",revision:"131969f76aac6258b7b5cecbce72b974"},{url:"/tower_game-master/assets/f4.png",revision:"66db0a37579f893ee537ac2b289b6039"},{url:"/tower_game-master/assets/f5.png",revision:"66db0a37579f893ee537ac2b289b6039"},{url:"/tower_game-master/assets/f6.png",revision:"ac1187e33d0ce22e552d2263b3cfccf7"},{url:"/tower_game-master/assets/f7.png",revision:"ae92207737dbf0a8227ec6215606b6b6"},{url:"/tower_game-master/assets/favicon.png",revision:"d29e03d53a6425806b20590e548c5a6d"},{url:"/tower_game-master/assets/game-over.mp3",revision:"77e51a4dac0d34bc98a09c6b01fdc900"},{url:"/tower_game-master/assets/game-over.ogg",revision:"478d5b0961cb758d8b2af421ed11fd32"},{url:"/tower_game-master/assets/heart.png",revision:"2b1f0265e715354d4a2627a9c1193eeb"},{url:"/tower_game-master/assets/hook.png",revision:"ce63ca74994e2e97258383dd1c07850c"},{url:"/tower_game-master/assets/main-bg.png",revision:"ab749ba87b0c603c4d2c617fa4cd0cd2"},{url:"/tower_game-master/assets/main-index-logo.png",revision:"50dd06ce5cb34e6f99046c5a1d2bf600"},{url:"/tower_game-master/assets/main-index-start.png",revision:"e278c22615cf92a4b69178e277dcd5e5"},{url:"/tower_game-master/assets/main-index-title.png",revision:"72cce4600fdfa1107c0a561e48af6154"},{url:"/tower_game-master/assets/main-loading-logo.png",revision:"a46a1d0a79699b84966efe5319c0be00"},{url:"/tower_game-master/assets/main-loading.gif",revision:"d33d37baaaa0f553c2244220158a846d"},{url:"/tower_game-master/assets/main-modal-again-b.png",revision:"4d138506c7e24d46ba8f81015b80fcf7"},{url:"/tower_game-master/assets/main-modal-bg.png",revision:"2690111498b3d3fbfb66ada127a4b971"},{url:"/tower_game-master/assets/main-modal-invite-b.png",revision:"e3c533db9794dff73b8ed9844adc17b8"},{url:"/tower_game-master/assets/main-modal-over.png",revision:"9026eb725f077ceaa5c0be044683cc67"},{url:"/tower_game-master/assets/main-share-icon.png",revision:"a9a9994ce32913b2ebfcdf227d4de423"},{url:"/tower_game-master/assets/rope.png",revision:"0eaa30298fe2f31056228f5ed1dcea7b"},{url:"/tower_game-master/assets/rotate.mp3",revision:"146905b6969524624126383ea98838aa"},{url:"/tower_game-master/assets/rotate.ogg",revision:"01187a587a595112af8f0b4a9d790213"},{url:"/tower_game-master/assets/score.png",revision:"a2c85b1980a6da4878004254d0459105"},{url:"/tower_game-master/assets/tutorial-arrow.png",revision:"37fa4f28da6c910cdddcd1edb1b666a7"},{url:"/tower_game-master/assets/tutorial.png",revision:"057f0ef24253000ed925803041768751"},{url:"/tower_game-master/assets/wenxue.eot",revision:"368819ca9232c4514bb8f67a58bdd626"},{url:"/tower_game-master/assets/wenxue.svg",revision:"55590ee1eacc03edc17ce9183af3d0f5"},{url:"/tower_game-master/assets/wenxue.ttf",revision:"18d0895c22fb372bc596193fbaed280f"},{url:"/tower_game-master/assets/wenxue.woff",revision:"c6e56486a133854334615e256db915b7"},{url:"/tower_game-master/assets/zepto-1.1.6.min.js",revision:"c44fff2aa423a40f8be83d6c34da381f"},{url:"/tower_game-master/dist/main.js",revision:"d374727a5c6f3c50f1deda9b915cb37e"},{url:"/tower_game-master/index.html",revision:"623d49abb38da6591c511ddc201f6ce0"},{url:"/tower_game-master/index.js",revision:"438c1ef8b83fc5a352b46cbcda8d10d9"},{url:"/tower_game-master/package-lock.json",revision:"9c3e8774e22b7a4d6c834f50d9ca600c"},{url:"/tower_game-master/package.json",revision:"b34bd7a3cabc45913c46f77dbee4b619"},{url:"/tower_game-master/src/animateFuncs.js",revision:"9bf9772a7f7fd77e8bc1b6d366d1996e"},{url:"/tower_game-master/src/background.js",revision:"bd20e8d3cce322f0e7267b6a3ab308d5"},{url:"/tower_game-master/src/block.js",revision:"730084df79ece546e53560f609c8ad19"},{url:"/tower_game-master/src/cloud.js",revision:"947ec3df917fe2be702e44cc321584e3"},{url:"/tower_game-master/src/constant.js",revision:"5319b97fd2fbe1bed1ca7ac8208a8609"},{url:"/tower_game-master/src/flight.js",revision:"57308cba144141e3b940e2ac017017b0"},{url:"/tower_game-master/src/hook.js",revision:"85ea645753d85352da27375523b78838"},{url:"/tower_game-master/src/index.js",revision:"3a7f2bb31e02eed668cd96d5f361adf3"},{url:"/tower_game-master/src/line.js",revision:"66c5bbb9d14ebac3e97f252554b379e5"},{url:"/tower_game-master/src/tutorial.js",revision:"396f64f5e447267c0cc76342204b99ad"},{url:"/tower_game-master/src/utils.js",revision:"6ea70ddc15b728f7ca91fcfac76a129f"},{url:"/vercel.svg",revision:"c0af2f507b369b085b35ef4bbe3bcf1e"},{url:"/window.svg",revision:"a2760511c65806022ad20adf74370ff3"}],{ignoreURLParametersMatching:[]}),e.cleanupOutdatedCaches(),e.registerRoute("/",new e.NetworkFirst({cacheName:"start-url",plugins:[{cacheWillUpdate:async({request:e,response:s,event:a,state:c})=>s&&"opaqueredirect"===s.type?new Response(s.body,{status:200,statusText:"OK",headers:s.headers}):s}]}),"GET"),e.registerRoute(/^https:\/\/fonts\.(?:gstatic)\.com\/.*/i,new e.CacheFirst({cacheName:"google-fonts-webfonts",plugins:[new e.ExpirationPlugin({maxEntries:4,maxAgeSeconds:31536e3})]}),"GET"),e.registerRoute(/^https:\/\/fonts\.(?:googleapis)\.com\/.*/i,new e.StaleWhileRevalidate({cacheName:"google-fonts-stylesheets",plugins:[new e.ExpirationPlugin({maxEntries:4,maxAgeSeconds:604800})]}),"GET"),e.registerRoute(/\.(?:eot|otf|ttc|ttf|woff|woff2|font.css)$/i,new e.StaleWhileRevalidate({cacheName:"static-font-assets",plugins:[new e.ExpirationPlugin({maxEntries:4,maxAgeSeconds:604800})]}),"GET"),e.registerRoute(/\.(?:jpg|jpeg|gif|png|svg|ico|webp)$/i,new e.StaleWhileRevalidate({cacheName:"static-image-assets",plugins:[new e.ExpirationPlugin({maxEntries:64,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\/_next\/image\?url=.+$/i,new e.StaleWhileRevalidate({cacheName:"next-image",plugins:[new e.ExpirationPlugin({maxEntries:64,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:mp3|wav|ogg)$/i,new e.CacheFirst({cacheName:"static-audio-assets",plugins:[new e.RangeRequestsPlugin,new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:mp4)$/i,new e.CacheFirst({cacheName:"static-video-assets",plugins:[new e.RangeRequestsPlugin,new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:js)$/i,new e.StaleWhileRevalidate({cacheName:"static-js-assets",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:css|less)$/i,new e.StaleWhileRevalidate({cacheName:"static-style-assets",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\/_next\/data\/.+\/.+\.json$/i,new e.StaleWhileRevalidate({cacheName:"next-data",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:json|xml|csv)$/i,new e.NetworkFirst({cacheName:"static-data-assets",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(({url:e})=>{if(!(self.origin===e.origin))return!1;const s=e.pathname;return!s.startsWith("/api/auth/")&&!!s.startsWith("/api/")},new e.NetworkFirst({cacheName:"apis",networkTimeoutSeconds:10,plugins:[new e.ExpirationPlugin({maxEntries:16,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(({url:e})=>{if(!(self.origin===e.origin))return!1;return!e.pathname.startsWith("/api/")},new e.NetworkFirst({cacheName:"others",networkTimeoutSeconds:10,plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(({url:e})=>!(self.origin===e.origin),new e.NetworkFirst({cacheName:"cross-origin",networkTimeoutSeconds:10,plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:3600})]}),"GET")}); +self.addEventListener('install', (event) => { + event.waitUntil(self.skipWaiting()); +}); + +self.addEventListener('activate', (event) => { + event.waitUntil(self.clients.claim()); +}); + +self.addEventListener('fetch', (event) => { + event.respondWith(fetch(event.request)); +}); diff --git a/src/app/[name]/api/logo/route.ts b/src/app/[name]/api/logo/route.ts index c9d20e7..c25a9cb 100644 --- a/src/app/[name]/api/logo/route.ts +++ b/src/app/[name]/api/logo/route.ts @@ -1,5 +1,6 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { fetchWithTimeout } from "@/lib/helpers/fetchWithTimeout"; +import { PWA_ICON_192 } from "@/lib/helpers/pwaIcons"; import { NextRequest, NextResponse } from "next/server"; import sharp from "sharp"; @@ -7,7 +8,7 @@ export const dynamic = "force-dynamic"; export const revalidate = 0; const FETCH_TIMEOUT_MS = 5000; -const DEFAULT_ICON = "/icons/web-app-manifest-192x192.png"; +const DEFAULT_ICON = PWA_ICON_192; export async function GET( request: NextRequest, diff --git a/src/app/[name]/layout.tsx b/src/app/[name]/layout.tsx index 81b5d18..5f66159 100644 --- a/src/app/[name]/layout.tsx +++ b/src/app/[name]/layout.tsx @@ -5,6 +5,7 @@ import { getRestaurant } from './lib/getRestaurant' import { notFound } from 'next/navigation' import CartChecker from '@/components/CartChecker' import ActiveChecker from '@/components/ActiveChecker' +import { PWA_ICON_192, PWA_ICON_512 } from '@/lib/helpers/pwaIcons' export const dynamic = 'force-dynamic' export const revalidate = 0 @@ -17,8 +18,8 @@ export async function generateMetadata({ params: Promise }): Promise { const { name } = await params - const defaultIcon = "/icons/web-app-manifest-192x192.png" - const defaultIcon512 = "/icons/web-app-manifest-512x512.png" + const defaultIcon = PWA_ICON_192 + const defaultIcon512 = PWA_ICON_512 try { const restaurant = await getRestaurant(name) @@ -56,12 +57,12 @@ export async function generateMetadata({ manifest: `/${name}/manifest.webmanifest`, icons: { icon: [ - { url: "/icons/web-app-manifest-192x192.png" }, - { url: "/icons/web-app-manifest-192x192.png", sizes: "192x192", type: "image/png" }, - { url: "/icons/web-app-manifest-512x512.png", sizes: "512x512", type: "image/png" }, + { url: PWA_ICON_192 }, + { url: PWA_ICON_192, sizes: "192x192", type: "image/png" }, + { url: PWA_ICON_512, sizes: "512x512", type: "image/png" }, ], - shortcut: "/icons/web-app-manifest-192x192.png", - apple: "/icons/web-app-manifest-192x192.png", + shortcut: PWA_ICON_192, + apple: PWA_ICON_192, }, } } diff --git a/src/app/[name]/manifest.webmanifest/route.ts b/src/app/[name]/manifest.webmanifest/route.ts index 796438f..ad0d87a 100644 --- a/src/app/[name]/manifest.webmanifest/route.ts +++ b/src/app/[name]/manifest.webmanifest/route.ts @@ -1,47 +1,18 @@ import { NextResponse } from "next/server"; +import { buildPwaManifestIcons } from "@/lib/helpers/pwaIcons"; import { getRestaurant } from "../lib/getRestaurant"; export const dynamic = "force-dynamic"; export const revalidate = 0; function buildManifestIcons(name: string, logo?: string | null) { - const default192 = "/icons/web-app-manifest-192x192.png"; - const default512 = "/icons/web-app-manifest-512x512.png"; - if (logo && logo.trim() !== "") { const logo192 = `/${name}/api/logo?url=${encodeURIComponent(logo)}&size=192`; const logo512 = `/${name}/api/logo?url=${encodeURIComponent(logo)}&size=512`; - - return [ - { - src: logo192, - sizes: "192x192", - type: "image/png", - purpose: "any", - }, - { - src: logo512, - sizes: "512x512", - type: "image/png", - purpose: "any", - }, - ]; + return buildPwaManifestIcons(logo192, logo512); } - return [ - { - src: default192, - sizes: "192x192", - type: "image/png", - purpose: "any", - }, - { - src: default512, - sizes: "512x512", - type: "image/png", - purpose: "any", - }, - ]; + return buildPwaManifestIcons(); } export async function GET( diff --git a/src/app/api/pwa-icon/[size]/route.ts b/src/app/api/pwa-icon/[size]/route.ts new file mode 100644 index 0000000..05293bd --- /dev/null +++ b/src/app/api/pwa-icon/[size]/route.ts @@ -0,0 +1,34 @@ +import { readFile } from 'fs/promises'; +import path from 'path'; +import sharp from 'sharp'; +import { NextRequest, NextResponse } from 'next/server'; + +const VALID_SIZES = [192, 512] as const; + +export async function GET( + _request: NextRequest, + { params }: { params: Promise<{ size: string }> }, +) { + const { size: sizeParam } = await params; + const size = parseInt(sizeParam, 10); + + if (!VALID_SIZES.includes(size as (typeof VALID_SIZES)[number])) { + return new NextResponse('Invalid size', { status: 400 }); + } + + try { + const svgPath = path.join(process.cwd(), 'public', 'icon0.svg'); + const svgBuffer = await readFile(svgPath); + const pngBuffer = await sharp(svgBuffer).resize(size, size).png().toBuffer(); + + return new NextResponse(new Uint8Array(pngBuffer), { + headers: { + 'Content-Type': 'image/png', + 'Cache-Control': 'public, max-age=31536000, immutable', + }, + }); + } catch (error) { + console.error('Error generating PWA icon:', error); + return new NextResponse('Failed to generate icon', { status: 500 }); + } +} diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 9325d26..2eba3e3 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -7,11 +7,11 @@ import initTranslations from '@/lib/i18n'; import TranslationsProvider from "@/components/utils/TranslationsProdiver"; import ToastContainer from "@/components/Toast"; import { ThemeColorSetter } from "@/components/ThemeColorSetter"; +import PwaServiceWorker from "@/components/PwaServiceWorker"; export const metadata: Metadata = { title: 'Dashboard', description: 'Webapp dashboard', - manifest: '/manifest.json', } export const viewport: Viewport = { @@ -54,6 +54,7 @@ export default async function RootLayout({ locale={locale} resources={resources}> +
{children}
diff --git a/src/components/PwaServiceWorker.tsx b/src/components/PwaServiceWorker.tsx new file mode 100644 index 0000000..c645f0a --- /dev/null +++ b/src/components/PwaServiceWorker.tsx @@ -0,0 +1,15 @@ +'use client'; + +import { useEffect } from 'react'; + +export default function PwaServiceWorker() { + useEffect(() => { + if (!('serviceWorker' in navigator)) return; + + navigator.serviceWorker.register('/sw.js', { scope: '/' }).catch(() => { + // Registration can fail on unsupported contexts (e.g. in-app browsers) + }); + }, []); + + return null; +} diff --git a/src/components/menu/SideMenu.tsx b/src/components/menu/SideMenu.tsx index bb92831..ec1f926 100644 --- a/src/components/menu/SideMenu.tsx +++ b/src/components/menu/SideMenu.tsx @@ -69,7 +69,16 @@ const isIOS = () => { const isStandalone = () => { if (typeof window === 'undefined') return false; - return ('standalone' in window.navigator) && (window.navigator as { standalone?: boolean }).standalone === true; + + const isIOSStandalone = + ('standalone' in window.navigator) && + (window.navigator as { standalone?: boolean }).standalone === true; + + const isDisplayModeStandalone = + window.matchMedia('(display-mode: standalone)').matches || + window.matchMedia('(display-mode: fullscreen)').matches; + + return isIOSStandalone || isDisplayModeStandalone; }; function SideMenu({ menuState, toggleMenuState, nightModeState, togglenightModeState }: Props) { @@ -114,18 +123,23 @@ function SideMenu({ menuState, toggleMenuState, nightModeState, togglenightModeS setIsIOSDevice(ios); setIsPWAInstalled(standalone); - if (!ios) { - const handleBeforeInstallPrompt = (e: Event) => { - e.preventDefault(); - setDeferredPrompt(e as BeforeInstallPromptEvent); - }; + const handleBeforeInstallPrompt = (e: Event) => { + e.preventDefault(); + setDeferredPrompt(e as BeforeInstallPromptEvent); + }; - window.addEventListener('beforeinstallprompt', handleBeforeInstallPrompt); + const handleAppInstalled = () => { + setDeferredPrompt(null); + setIsPWAInstalled(true); + }; - return () => { - window.removeEventListener('beforeinstallprompt', handleBeforeInstallPrompt); - }; - } + window.addEventListener('beforeinstallprompt', handleBeforeInstallPrompt); + window.addEventListener('appinstalled', handleAppInstalled); + + return () => { + window.removeEventListener('beforeinstallprompt', handleBeforeInstallPrompt); + window.removeEventListener('appinstalled', handleAppInstalled); + }; }, []); const handleInstallPWA = async () => { diff --git a/src/components/wrapper/PreferenceWrapper.tsx b/src/components/wrapper/PreferenceWrapper.tsx index ce433d2..bd80030 100644 --- a/src/components/wrapper/PreferenceWrapper.tsx +++ b/src/components/wrapper/PreferenceWrapper.tsx @@ -2,10 +2,19 @@ import usePreference from '@/hooks/helpers/usePreference'; import React, { useCallback, useEffect, useState } from 'react'; +import { useParams } from 'next/navigation'; import { useGetAbout } from '@/app/[name]/(Main)/about/hooks/useAboutData'; import SplashScreen from '@/components/overlays/SplashScreen'; import { hexToOklch, calculateBrightness } from '@/lib/helpers/colorUtils'; +function getSplashStorageKey(restaurantSlug: string) { + return `splash-seen:${restaurantSlug}`; +} + +function getSplashFingerprint(logo?: string | null, name?: string | null) { + return `${logo ?? ''}|${name ?? ''}`; +} + type Props = { children: React.ReactNode } @@ -24,6 +33,9 @@ function applyPrimaryColor(colorHex: string) { } function PreferenceWrapper({ children }: Props) { + const params = useParams(); + const restaurantSlug = typeof params.name === 'string' ? params.name : ''; + const { state: nightMode } = usePreference('night-mode', false); const { data: aboutData } = useGetAbout(); @@ -34,7 +46,14 @@ function PreferenceWrapper({ children }: Props) { const handleSplashDismiss = useCallback(() => { setShowSplash(false); - }, []); + + if (!restaurantSlug) return; + + const logo = aboutData?.data?.logo || cachedLogo; + const name = aboutData?.data?.name || cachedName; + const fingerprint = getSplashFingerprint(logo, name); + localStorage.setItem(getSplashStorageKey(restaurantSlug), fingerprint); + }, [aboutData?.data?.logo, aboutData?.data?.name, cachedLogo, cachedName, restaurantSlug]); useEffect(() => { setMounted(true); @@ -49,16 +68,15 @@ function PreferenceWrapper({ children }: Props) { if (savedLogo) setCachedLogo(savedLogo); if (savedName) setCachedName(savedName); - const navigationEntries = performance.getEntriesByType('navigation') as PerformanceNavigationTiming[]; - const isReload = navigationEntries.length > 0 && navigationEntries[0].type === 'reload'; - const isFirstLoadInSession = !sessionStorage.getItem('app-loaded') || isReload; + if (!restaurantSlug) return; - if (isFirstLoadInSession) { + const currentFingerprint = getSplashFingerprint(savedLogo, savedName); + const seenFingerprint = localStorage.getItem(getSplashStorageKey(restaurantSlug)); + + if (seenFingerprint !== currentFingerprint) { setShowSplash(true); } - - sessionStorage.setItem('app-loaded', 'true'); - }, []); + }, [restaurantSlug]); useEffect(() => { if (aboutData?.data?.menuColor) { @@ -76,6 +94,19 @@ function PreferenceWrapper({ children }: Props) { // eslint-disable-next-line react-hooks/exhaustive-deps }, [aboutData?.data?.menuColor]); + useEffect(() => { + if (!restaurantSlug || showSplash) return; + + const logo = aboutData?.data?.logo || cachedLogo; + const name = aboutData?.data?.name || cachedName; + const fingerprint = getSplashFingerprint(logo, name); + const seenFingerprint = localStorage.getItem(getSplashStorageKey(restaurantSlug)); + + if (seenFingerprint === '|' && fingerprint !== '|') { + localStorage.setItem(getSplashStorageKey(restaurantSlug), fingerprint); + } + }, [aboutData?.data?.logo, aboutData?.data?.name, cachedLogo, cachedName, restaurantSlug, showSplash]); + useEffect(() => { document.documentElement.setAttribute('data-theme', nightMode ? 'dark' : 'light'); }, [nightMode]); diff --git a/src/lib/helpers/pwaIcons.ts b/src/lib/helpers/pwaIcons.ts new file mode 100644 index 0000000..754de36 --- /dev/null +++ b/src/lib/helpers/pwaIcons.ts @@ -0,0 +1,34 @@ +export const PWA_ICON_192 = '/api/pwa-icon/192'; +export const PWA_ICON_512 = '/api/pwa-icon/512'; + +export function buildPwaManifestIcons(logo192?: string, logo512?: string) { + const icon192 = logo192 ?? PWA_ICON_192; + const icon512 = logo512 ?? PWA_ICON_512; + + return [ + { + src: icon192, + sizes: '192x192', + type: 'image/png', + purpose: 'any', + }, + { + src: icon512, + sizes: '512x512', + type: 'image/png', + purpose: 'any', + }, + { + src: icon192, + sizes: '192x192', + type: 'image/png', + purpose: 'maskable', + }, + { + src: icon512, + sizes: '512x512', + type: 'image/png', + purpose: 'maskable', + }, + ]; +}