Skip to content

Commit 4602eaa

Browse files
authored
Merge pull request #96 from tridecco/feature/95-enhance-offline-experience-with-fallback-page-and-service-worker-pre-caching
2 parents 40241c1 + 6d5e7ff commit 4602eaa

5 files changed

Lines changed: 211 additions & 4 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ Tridecco Game Client is a frontend application designed to support Tridecco game
5656
DEV_SERVER_PROXY_TARGET=http://localhost:5000 # Development server proxy target (default: http://localhost:5000)
5757
```
5858

59-
> **Note**: The `BUILD_MODE` variable is used to determine the build mode. It can be `web` or `local`. When set to `web`, the client will load static assets from the CDN. When set to `local`, the client will load static assets from the local server. The `local` build mode is used for native builds (e.g. Electron, Capacitor, etc.).
59+
> **Note**: The `BUILD_MODE` variable is used to determine the build mode. It can be `web` or `local`. When set to `web`, the client will load static assets from the CDN. When set to `local`, the client will load static assets from the local server, and Service Workers will be disabled. The `local` build mode is used for native builds (e.g. Electron, Capacitor, etc.).
6060
6161
4. Build the client:
6262

src/partials/head.ejs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,7 @@
22
<meta name="viewport" content="width=device-width, initial-scale=1.0">
33
<link rel="icon" type="image/x-icon" href="/favicon.ico">
44
<link rel="stylesheet" href="<%= env.CDN_URL %>/css/styles.css">
5+
<% if (env.BUILD_MODE !== 'local') { %>
56
<%- include('./service-worker.ejs') %>
7+
<% } %>
68
<script src="<%= env.CDN_URL %>/js/app.js"></script>

src/scripts/app.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -443,7 +443,7 @@ class Location {
443443
* @returns {boolean} - True if the route is public, false otherwise.
444444
*/
445445
isPublicRoute(route) {
446-
return ['/', '/more', '/404', '/403', '/leaderboards'].includes(route);
446+
return ['/', '/more', '/404', '/403', '/leaderboards', '/offline'].includes(route);
447447
}
448448

449449
/**

src/scripts/service-worker.js

Lines changed: 157 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,149 @@
66
const DYNAMIC_CACHE = 'dynamic';
77
const STATIC_CACHE = 'static';
88

9+
const PRECACHE_ASSETS = [
10+
'/',
11+
'/offline',
12+
'/login',
13+
'/logout',
14+
'/register',
15+
'/password-reset',
16+
'/user',
17+
'/user/edit',
18+
'/user/account',
19+
'/user/settings',
20+
'/user/security',
21+
'/more',
22+
23+
'/favicon.ico',
24+
'/css/styles.css',
25+
'/js/app.js',
26+
'/js/auth.js',
27+
'/js/user.js',
28+
'/img/default-avatar.svg',
29+
30+
'/img/backgrounds/tridecco.jpg',
31+
'/img/backgrounds/wooden-board.jpg',
32+
'/img/backgrounds/broken-glass.jpg',
33+
'/img/backgrounds/composite-board.jpg',
34+
'/img/backgrounds/frosted-glass.jpg',
35+
'/img/backgrounds/galaxy.jpg',
36+
'/img/backgrounds/gold-leaf.jpg',
37+
'/img/backgrounds/leather.jpg',
38+
'/img/backgrounds/log.jpg',
39+
'/img/backgrounds/marble.jpg',
40+
'/img/backgrounds/metal-plate.jpg',
41+
'/img/backgrounds/sand.jpg',
42+
'/img/ranks/bg/iron.png',
43+
'/img/ranks/bg/bronze.png',
44+
'/img/ranks/bg/silver.png',
45+
'/img/ranks/bg/gold.png',
46+
'/img/ranks/bg/platinum.png',
47+
'/img/ranks/bg/diamond.png',
48+
'/img/ranks/bg/tridecco.png',
49+
'/img/ranks/nobg/iron.png',
50+
'/img/ranks/nobg/bronze.png',
51+
'/img/ranks/nobg/silver.png',
52+
'/img/ranks/nobg/gold.png',
53+
'/img/ranks/nobg/platinum.png',
54+
'/img/ranks/nobg/diamond.png',
55+
'/img/ranks/nobg/tridecco.png',
56+
57+
'/libs/tridecco-board/0.4.2/tridecco-board.min.js',
58+
'/libs/tridecco-board/0.4.2/assets/grids/black.png',
59+
'/libs/tridecco-board/0.4.2/assets/grids/blue.png',
60+
'/libs/tridecco-board/0.4.2/assets/grids/cyan.png',
61+
'/libs/tridecco-board/0.4.2/assets/grids/green.png',
62+
'/libs/tridecco-board/0.4.2/assets/grids/magenta.png',
63+
'/libs/tridecco-board/0.4.2/assets/grids/red.png',
64+
'/libs/tridecco-board/0.4.2/assets/grids/white.png',
65+
'/libs/tridecco-board/0.4.2/assets/grids/yellow.png',
66+
'/libs/tridecco-board/0.4.2/assets/textures/classic/normal/index.json',
67+
'/libs/tridecco-board/0.4.2/assets/textures/classic/normal/hexagons/blue.png',
68+
'/libs/tridecco-board/0.4.2/assets/textures/classic/normal/hexagons/red.png',
69+
'/libs/tridecco-board/0.4.2/assets/textures/classic/normal/hexagons/white.png',
70+
'/libs/tridecco-board/0.4.2/assets/textures/classic/normal/hexagons/yellow.png',
71+
'/libs/tridecco-board/0.4.2/assets/textures/classic/normal/tiles/empty-flipped.png',
72+
'/libs/tridecco-board/0.4.2/assets/textures/classic/normal/tiles/empty.png',
73+
'/libs/tridecco-board/0.4.2/assets/textures/classic/normal/tiles/blue-white-flipped.png',
74+
'/libs/tridecco-board/0.4.2/assets/textures/classic/normal/tiles/blue-white.png',
75+
'/libs/tridecco-board/0.4.2/assets/textures/classic/normal/tiles/red-blue-flipped.png',
76+
'/libs/tridecco-board/0.4.2/assets/textures/classic/normal/tiles/red-blue.png',
77+
'/libs/tridecco-board/0.4.2/assets/textures/classic/normal/tiles/red-yellow-flipped.png',
78+
'/libs/tridecco-board/0.4.2/assets/textures/classic/normal/tiles/red-yellow.png',
79+
'/libs/tridecco-board/0.4.2/assets/textures/classic/normal/tiles/white-red-flipped.png',
80+
'/libs/tridecco-board/0.4.2/assets/textures/classic/normal/tiles/white-red.png',
81+
'/libs/tridecco-board/0.4.2/assets/textures/classic/normal/tiles/yellow-blue-flipped.png',
82+
'/libs/tridecco-board/0.4.2/assets/textures/classic/normal/tiles/yellow-blue.png',
83+
'/libs/tridecco-board/0.4.2/assets/textures/classic/rgbblind/index.json',
84+
'/libs/tridecco-board/0.4.2/assets/textures/classic/rgbblind/hexagons/blue.png',
85+
'/libs/tridecco-board/0.4.2/assets/textures/classic/rgbblind/hexagons/red.png',
86+
'/libs/tridecco-board/0.4.2/assets/textures/classic/rgbblind/hexagons/white.png',
87+
'/libs/tridecco-board/0.4.2/assets/textures/classic/rgbblind/hexagons/yellow.png',
88+
'/libs/tridecco-board/0.4.2/assets/textures/classic/rgbblind/tiles/empty-flipped.png',
89+
'/libs/tridecco-board/0.4.2/assets/textures/classic/rgbblind/tiles/empty.png',
90+
'/libs/tridecco-board/0.4.2/assets/textures/classic/rgbblind/tiles/blue-white-flipped.png',
91+
'/libs/tridecco-board/0.4.2/assets/textures/classic/rgbblind/tiles/blue-white.png',
92+
'/libs/tridecco-board/0.4.2/assets/textures/classic/rgbblind/tiles/red-blue-flipped.png',
93+
'/libs/tridecco-board/0.4.2/assets/textures/classic/rgbblind/tiles/red-blue.png',
94+
'/libs/tridecco-board/0.4.2/assets/textures/classic/rgbblind/tiles/red-yellow-flipped.png',
95+
'/libs/tridecco-board/0.4.2/assets/textures/classic/rgbblind/tiles/red-yellow.png',
96+
'/libs/tridecco-board/0.4.2/assets/textures/classic/rgbblind/tiles/white-red-flipped.png',
97+
'/libs/tridecco-board/0.4.2/assets/textures/classic/rgbblind/tiles/white-red.png',
98+
'/libs/tridecco-board/0.4.2/assets/textures/classic/rgbblind/tiles/yellow-blue-flipped.png',
99+
'/libs/tridecco-board/0.4.2/assets/textures/classic/rgbblind/tiles/yellow-blue.png',
100+
'/libs/tridecco-board/0.4.2/assets/textures/classic/monochrome/index.json',
101+
'/libs/tridecco-board/0.4.2/assets/textures/classic/monochrome/hexagons/blue.png',
102+
'/libs/tridecco-board/0.4.2/assets/textures/classic/monochrome/hexagons/red.png',
103+
'/libs/tridecco-board/0.4.2/assets/textures/classic/monochrome/hexagons/white.png',
104+
'/libs/tridecco-board/0.4.2/assets/textures/classic/monochrome/hexagons/yellow.png',
105+
'/libs/tridecco-board/0.4.2/assets/textures/classic/monochrome/tiles/empty-flipped.png',
106+
'/libs/tridecco-board/0.4.2/assets/textures/classic/monochrome/tiles/empty.png',
107+
'/libs/tridecco-board/0.4.2/assets/textures/classic/monochrome/tiles/blue-white-flipped.png',
108+
'/libs/tridecco-board/0.4.2/assets/textures/classic/monochrome/tiles/blue-white.png',
109+
'/libs/tridecco-board/0.4.2/assets/textures/classic/monochrome/tiles/red-blue-flipped.png',
110+
'/libs/tridecco-board/0.4.2/assets/textures/classic/monochrome/tiles/red-blue.png',
111+
'/libs/tridecco-board/0.4.2/assets/textures/classic/monochrome/tiles/red-yellow-flipped.png',
112+
'/libs/tridecco-board/0.4.2/assets/textures/classic/monochrome/tiles/red-yellow.png',
113+
'/libs/tridecco-board/0.4.2/assets/textures/classic/monochrome/tiles/white-red-flipped.png',
114+
'/libs/tridecco-board/0.4.2/assets/textures/classic/monochrome/tiles/white-red.png',
115+
'/libs/tridecco-board/0.4.2/assets/textures/classic/monochrome/tiles/yellow-blue-flipped.png',
116+
'/libs/tridecco-board/0.4.2/assets/textures/classic/monochrome/tiles/yellow-blue.png',
117+
'/libs/tridecco-board/0.4.2/assets/backgrounds/wooden-board.jpg',
118+
'/libs/tridecco-board/0.4.2/assets/backgrounds/broken-glass.jpg',
119+
'/libs/tridecco-board/0.4.2/assets/backgrounds/composite-board.jpg',
120+
'/libs/tridecco-board/0.4.2/assets/backgrounds/frosted-glass.jpg',
121+
'/libs/tridecco-board/0.4.2/assets/backgrounds/galaxy.jpg',
122+
'/libs/tridecco-board/0.4.2/assets/backgrounds/gold-leaf.jpg',
123+
'/libs/tridecco-board/0.4.2/assets/backgrounds/leather.jpg',
124+
'/libs/tridecco-board/0.4.2/assets/backgrounds/log.jpg',
125+
'/libs/tridecco-board/0.4.2/assets/backgrounds/marble.jpg',
126+
'/libs/tridecco-board/0.4.2/assets/backgrounds/metal-plate.jpg',
127+
'/libs/tridecco-board/0.4.2/assets/backgrounds/sand.jpg',
128+
];
129+
9130
self.addEventListener('install', (event) => {
10-
event.waitUntil(self.skipWaiting());
131+
event.waitUntil(
132+
caches
133+
.open(STATIC_CACHE)
134+
.then((cache) => cache.addAll(PRECACHE_ASSETS))
135+
.then(() => self.skipWaiting()),
136+
);
11137
});
12138

13139
self.addEventListener('activate', (event) => {
14-
event.waitUntil(clients.claim());
140+
event.waitUntil(
141+
caches
142+
.keys()
143+
.then((keys) =>
144+
Promise.all(
145+
keys
146+
.filter((key) => key !== STATIC_CACHE && key !== DYNAMIC_CACHE)
147+
.map((key) => caches.delete(key)),
148+
),
149+
)
150+
.then(() => self.clients.claim()),
151+
);
15152
});
16153

17154
self.addEventListener('fetch', (event) => {
@@ -41,6 +178,24 @@ self.addEventListener('fetch', (event) => {
41178
return;
42179
}
43180

181+
if (req.mode === 'navigate') {
182+
event.respondWith(
183+
fetch(req)
184+
.then((networkRes) => {
185+
return caches.open(DYNAMIC_CACHE).then((cache) => {
186+
cache.put(req, networkRes.clone());
187+
return networkRes;
188+
});
189+
})
190+
.catch(() =>
191+
caches
192+
.match(req)
193+
.then((cachedRes) => cachedRes || caches.match('/offline')),
194+
),
195+
);
196+
return;
197+
}
198+
44199
event.respondWith(
45200
fetch(req)
46201
.then((networkRes) => {

src/views/offline/index.ejs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
4+
<head>
5+
<%- include("../../partials/head.ejs") %>
6+
<title>Tridecco | Offline</title>
7+
</head>
8+
9+
<body class="h-screen overflow-hidden" style="background: url('<%= env.CDN_URL %>/img/backgrounds/wooden-board.jpg') center center / cover no-repeat;">
10+
<!-- Main Layout Container -->
11+
<div class="fixed inset-0 flex flex-col h-screen text-gray-800 z-20">
12+
13+
<!-- Centered Content Container -->
14+
<main class="flex flex-1 flex-col items-center justify-center p-4">
15+
16+
<div class="bg-white/80 backdrop-blur-sm rounded-xl shadow-xl border border-amber-700/30 px-8 py-10 max-w-md w-full flex flex-col items-center">
17+
<!-- Icon -->
18+
<svg class="w-16 h-16 mb-4 text-amber-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
19+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z" />
20+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 11a3 3 0 11-6 0 3 3 0 016 0z" />
21+
</svg>
22+
23+
<!-- Title -->
24+
<h1 class="text-2xl font-bold text-amber-900 mb-2 text-center">You Are Offline</h1>
25+
26+
<!-- Description -->
27+
<p class="text-gray-700 mb-6 text-center">
28+
It looks like you have lost your internet connection.<br>
29+
Please check your network and try again.
30+
</p>
31+
32+
<!-- Return Home Button -->
33+
<a href="/" class="bg-emerald-600 hover:bg-emerald-700 text-white font-bold py-2 px-6 rounded-md focus:outline-none focus:shadow-outline transition duration-200 text-sm">
34+
Return to Home
35+
</a>
36+
</div>
37+
38+
</main>
39+
40+
<!-- Footer -->
41+
<footer class="h-8 bg-black/30 backdrop-blur-sm border-t border-amber-700/50 z-10 flex items-center justify-between px-4 text-xs text-gray-400 flex-shrink-0">
42+
<script>
43+
document.write('<div>Tridecco © ' + new Date().getFullYear() + '</div>');
44+
</script>
45+
</footer>
46+
47+
</div>
48+
</body>
49+
50+
</html>

0 commit comments

Comments
 (0)