Skip to content

Commit f03dafe

Browse files
committed
Added support to install as a PWM
As a side-effect, added offline mode support (after one page reload) Progressive Web App: https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps
1 parent fd87d91 commit f03dafe

19 files changed

+315
-14
lines changed

res/banner.jpg app/banner.jpg

File renamed without changes.

app/manifest.json

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
{
2+
"name": "huroutes",
3+
"short_name": "huroutes",
4+
"lang": "hu",
5+
"description": "Magyar utak vezetésre",
6+
"display": "standalone",
7+
"icons": [
8+
{
9+
"src": "../res/favicon-192.png",
10+
"sizes": "192x192",
11+
"type": "image/png"
12+
},
13+
{
14+
"src": "../res/favicon-512.png",
15+
"sizes": "512x512",
16+
"type": "image/png"
17+
},
18+
{
19+
"src": "../app/maskableicon-180.png",
20+
"sizes": "180x180",
21+
"type": "image/png",
22+
"purpose": "maskable"
23+
},
24+
{
25+
"src": "../app/maskableicon-192.png",
26+
"sizes": "192x192",
27+
"type": "image/png",
28+
"purpose": "maskable"
29+
},
30+
{
31+
"src": "../app/maskableicon-512.png",
32+
"sizes": "512x512",
33+
"type": "image/png",
34+
"purpose": "maskable"
35+
}
36+
],
37+
"start_url": "../",
38+
"scope": "../",
39+
"theme_color": "#0000bb",
40+
"background_color": "#13192e",
41+
"categories": [
42+
"navigation", "travel"
43+
]
44+
}

app/maskableicon-180.png

26.2 KB
Loading

app/maskableicon-192.png

26.7 KB
Loading

app/maskableicon-512.png

157 KB
Loading

app/mstile-150.png

16.5 KB
Loading

app/mstile.xml

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<browserconfig>
3+
<msapplication>
4+
<tile>
5+
<square150x150logo src="app/mstile-150.png"/>
6+
<TileColor>#2b5797</TileColor>
7+
</tile>
8+
</msapplication>
9+
</browserconfig>

app/safari-pinned-tab.svg

+81
Loading

index.html

+51-8
Original file line numberDiff line numberDiff line change
@@ -10,24 +10,33 @@
1010
<!-- Disable resizing the viewport for mobile -->
1111
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
1212

13+
<title>huroutes | Magyar utak vezetésre</title>
14+
15+
<!-- Web app icons and themes -->
16+
<link rel="icon" type="image/png" sizes="32x32" href="res/favicon-32.png">
17+
<link rel="icon" type="image/png" sizes="16x16" href="res/favicon-16.png">
18+
<link rel="shortcut icon" href="res/favicon.ico">
19+
<link rel="manifest" href="app/manifest.json">
20+
<link rel="mask-icon" href="app/safari-pinned-tab.svg" color="#bae1c7">
21+
<link rel="apple-touch-icon" sizes="180x180" href="app/maskableicon-180.png">
22+
<meta name="application-name" content="huroutes">
23+
<meta name="apple-mobile-web-app-title" content="huroutes">
24+
<meta name="msapplication-TileColor" content="#2b5797">
25+
<meta name="msapplication-config" content="app/mstile.xml">
26+
<meta name="theme-color" content="#0000bb">
27+
1328
<!-- Shared link preview card resources -->
1429
<meta property="og:title" content="huroutes" />
1530
<meta property="og:type" content="website" />
1631
<meta property="og:url" content="https://sp3eder.github.io/huroutes/" />
1732
<meta property="og:description" content="Magyar utak vezetésre" />
18-
<meta property="og:image" content="https://sp3eder.github.io/huroutes/res/banner.jpg" />
19-
<meta property="og:image:secure_url" content="https://sp3eder.github.io/huroutes/res/banner.jpg" />
33+
<meta property="og:image" content="https://sp3eder.github.io/huroutes/app/banner.jpg" />
34+
<meta property="og:image:secure_url" content="https://sp3eder.github.io/huroutes/app/banner.jpg" />
2035
<meta property="og:image:alt" content="huroutes" />
2136
<meta property="og:image:type" content="image/jpeg" />
2237
<meta property="og:image:width" content="1200" />
2338
<meta property="og:image:height" content="630" />
2439

25-
<title>huroutes | Magyar utak vezetésre</title>
26-
27-
<link rel="icon" href="res/favicon.ico" />
28-
<link rel="icon" href="res/favicon.png" sizes="32x32" type="image/png" />
29-
<link rel="icon" href="res/favicon-sm.png" sizes="16x16" type="image/png" />
30-
3140
<!-- Google tag (gtag.js) -->
3241
<script async src="https://www.googletagmanager.com/gtag/js?id=G-7RW997RX2F"></script>
3342
<script>
@@ -37,6 +46,11 @@
3746
gtag('config', 'G-7RW997RX2F');
3847
</script>
3948

49+
<script>
50+
if ('serviceWorker' in navigator)
51+
navigator.serviceWorker.register('sw.js');
52+
</script>
53+
4054
<!-- 3rd party library CSS -->
4155
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/css/bootstrap.min.css" integrity="sha256-+IZRbz1B6ee9mUx/ejmonK+ulIP5A5bLDd6v6NHqXnI=" crossorigin="anonymous" referrerpolicy="no-referrer">
4256
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@forevolve/bootstrap-dark@4.0.0/dist/css/toggle-bootstrap.min.css" integrity="sha256-FrYhUW1FIy6GrbYaAdOOeWzysJaYHvXRTvC5JkpI2Io=" crossorigin="anonymous" referrerpolicy="no-referrer">
@@ -201,6 +215,35 @@ <h3>Beállítások</h3>
201215
<a href="https://github.com/Sp3EdeR/huroutes-android/releases" target="_blank">innen töltheted le</a>.
202216
</div>
203217
</div>
218+
<!-- Progressive Web App ad toast -->
219+
<div id="toast-pwa-app" role="alert" aria-live="assertive" aria-atomic="true" class="toast hide" data-autohide="false">
220+
<div class="toast-header">
221+
<strong class="mr-auto">Alkalmazásként Telepítés</strong>
222+
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
223+
<span aria-hidden="true">&times;</span>
224+
</button>
225+
</div>
226+
<div class="toast-body">
227+
<p>Telepíteni tudod ezt az alkalmazást rögtön a böngésződből az eszközödre.
228+
Ha ki szeretnéd próbálni, <a href="#">kattints ide</a>.</p>
229+
<p>Ha később szeretnéd kipróbálni, a böngésződ címsorában vagy menüjében találhatod
230+
meg a telepítés vagy letöltés menüpontot legközelebb.</p>
231+
</div>
232+
</div>
233+
<!-- Safari PWA ad toast -->
234+
<div id="toast-safari-app" role="alert" aria-live="assertive" aria-atomic="true" class="toast hide" data-autohide="false">
235+
<div class="toast-header">
236+
<strong class="mr-auto">Alkalmazásként Telepítés</strong>
237+
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
238+
<span aria-hidden="true">&times;</span>
239+
</button>
240+
</div>
241+
<div class="toast-body">
242+
Telepíteni tudod ezt az alkalmazást rögtön a böngésződből az eszközödre.
243+
Ha ki szeretnéd próbálni, kattints a megosztás <i class="fas fa-external-link-alt"></i>
244+
ikonra, majd válaszd ki a "Hozzáadás a kezdőképernyőhöz" lehetőséget.
245+
</div>
246+
</div>
204247
<!-- App update toast -->
205248
<div id="toast-app-update" role="alert" aria-live="assertive" aria-atomic="true" class="toast hide" data-autohide="false">
206249
<div class="toast-header">

linkmaker.html

+4-3
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@
55

66
<title>huroutes | Helyjelölő link készítése</title>
77

8-
<link rel="icon" href="res/favicon.ico" />
9-
<link rel="icon" href="res/favicon.png" sizes="32x32" type="image/png" />
10-
<link rel="icon" href="res/favicon-sm.png" sizes="16x16" type="image/png" />
8+
<link rel="icon" type="image/png" sizes="32x32" href="res/favicon-32.png">
9+
<link rel="icon" type="image/png" sizes="16x16" href="res/favicon-16.png">
10+
<link rel="shortcut icon" href="res/favicon.ico">
11+
<link rel="mask-icon" href="app/safari-pinned-tab.svg" color="#bae1c7">
1112

1213
<script>
1314
webRoot = 'https://sp3eder.github.io/huroutes/';

res/favicon-16.png

575 Bytes
Loading

res/favicon-192.png

33.7 KB
Loading

res/favicon-32.png

1.47 KB
Loading

res/favicon-512.png

217 KB
Loading

res/favicon-sm.png

-502 Bytes
Binary file not shown.

res/favicon.ico

13.6 KB
Binary file not shown.

res/favicon.png

-998 Bytes
Binary file not shown.

res/huroutes.js

+62-3
Original file line numberDiff line numberDiff line change
@@ -1138,8 +1138,17 @@ function initAdToast()
11381138
/** Shows a toast to Android users that don't use the app that it is available. */
11391139
function androidAppToast()
11401140
{
1141-
if (localStorage.shownAndroidAd || !navigator.userAgent.includes('Android') ||
1142-
navigator.userAgent.includes('huroutes'))
1141+
// Don't show if:
1142+
if (
1143+
// Already shown the ad
1144+
localStorage.shownAndroidAd ||
1145+
// Not running on Android
1146+
!navigator.userAgent.includes('Android') ||
1147+
// Running within the Android application
1148+
navigator.userAgent.includes('huroutes') ||
1149+
// Running within PWA application
1150+
navigator.standalone === true ||
1151+
window.matchMedia('(display-mode: standalone)').matches)
11431152
return;
11441153

11451154
var androidToast = $('#toast-android-app');
@@ -1252,4 +1261,54 @@ function getJSON(url, ...args)
12521261
});
12531262
}
12541263

1255-
})();
1264+
})(); // End of main code
1265+
1266+
// Progressive Web Application code; separated closure
1267+
(function () {
1268+
1269+
// Opera desktop doesn't support PWA, but pops up beforeinstallprompt.
1270+
if (navigator.userAgent.match(/(Windows|Macintosh|X11).*(Opera|OPR)/))
1271+
return;
1272+
1273+
function showToast(id)
1274+
{
1275+
var toast = $(id);
1276+
toast.toast('show');
1277+
// The toast is not shown again once closed.
1278+
toast.on('hide.bs.toast', () => localStorage.shownPwaAd = true);
1279+
return toast;
1280+
}
1281+
1282+
function pwaToast(event)
1283+
{
1284+
// Prevents default browser notification https://developer.mozilla.org/en-US/docs/Web/API/BeforeInstallPromptEvent
1285+
event.preventDefault();
1286+
var toast = showToast('#toast-pwa-app');
1287+
toast.find('a[href]').on('click', () => {
1288+
// https://developer.mozilla.org/en-US/docs/Web/API/BeforeInstallPromptEvent/prompt
1289+
event.prompt();
1290+
toast.toast('hide');
1291+
return false;
1292+
});
1293+
}
1294+
1295+
if (!localStorage.shownPwaAd)
1296+
{
1297+
// Safari sucks, it is incompatible
1298+
sfrTime = -1;
1299+
if (navigator.userAgent && navigator.userAgent.match(
1300+
// Test for safari on iOS, but not Chrome-based or Firefox frontend
1301+
/(?:iP(?:od|ad|hone))(?!.*(?:CriOS|FxiOS))/) &&
1302+
// Ensure it doesn't pop in standalone mode
1303+
!navigator.standalone && !window.matchMedia('(display-mode: standalone)').matches)
1304+
{
1305+
sfrTime = setTimeout(() => showToast('#toast-safari-app'), 5000);
1306+
}
1307+
1308+
window.addEventListener("beforeinstallprompt", event => {
1309+
setTimeout(() => pwaToast(event), 5000);
1310+
clearTimeout(sfrTime);
1311+
});
1312+
}
1313+
1314+
})(); // End of PWA code

sw.js

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
(function () {
2+
3+
cacheNames = {
4+
thirdparty: "thirdparty-v1",
5+
static: "static-v1",
6+
dynamic: "dynamic-v1"
7+
};
8+
9+
// Delete caches not in the list of names
10+
self.addEventListener('activate', e => {
11+
cacheIds = Object.keys(cacheNames).map(key => cacheNames[key]);
12+
e.waitUntil(
13+
caches.keys()
14+
.then(names => names.filter(name => !cacheIds.includes(name)))
15+
.then(names => Promise.all(names.map(name => caches.delete(name))))
16+
.then(() => self.clients.claim())
17+
);
18+
});
19+
20+
// Cache logic implementation on fetch
21+
self.addEventListener('fetch', e => {
22+
const url = new URL(e.request.url);
23+
24+
// This caching logic serves first from cache, then falls back to net
25+
const cacheFirst = async cacheName =>
26+
caches.open(cacheName)
27+
.then(cache => cache.match(e.request.url)
28+
.then(cachedResponse => cachedResponse ||
29+
fetch(e.request)
30+
.then(response => {
31+
cache.put(e.request, response.clone());
32+
return response;
33+
})
34+
));
35+
36+
// This caching logic serves first from net, then falls back to cache
37+
const netFirst = async cacheName =>
38+
caches.open(cacheName)
39+
.then(cache => {
40+
return fetch(e.request)
41+
.then(response => {
42+
cache.put(e.request, response.clone());
43+
return response;
44+
})
45+
.catch(() => cache.match(e.request.url));
46+
});
47+
48+
// Requests for huroutes resources
49+
if (url.origin == self.location.origin)
50+
{
51+
// Cache huroutes images to the static cache and serve cache-first
52+
if (e.request.destination === 'image')
53+
return e.respondWith(cacheFirst(cacheNames.static));
54+
55+
// Cache other huroutes stuff to the dynamic cache and serve net-first
56+
return e.respondWith(netFirst(cacheNames.dynamic));
57+
}
58+
59+
// Cache CDN stuff to the thirdparty cache and serve cache-first
60+
if (url.hostname == "cdn.jsdelivr.net")
61+
return e.respondWith(cacheFirst(cacheNames.thirdparty));
62+
});
63+
64+
})();

0 commit comments

Comments
 (0)