Offline PWA works again!
This commit is contained in:
parent
9f1ee8ecd6
commit
6e24a5c760
|
@ -98,7 +98,7 @@
|
|||
<FilesMatch "\.(js|css|woff|woff2|ttf|eot)$">
|
||||
Header set Cache-Control "max-age=2592000, public"
|
||||
</FilesMatch>
|
||||
<FilesMatch "\\.(jpe?g|png|gif|swf|flv|pdf|svg)$">
|
||||
<FilesMatch "\.(jpe?g|png|gif|swf|flv|pdf|svg|ico)$">
|
||||
Header set Cache-Control "max-age=604800, public"
|
||||
</FilesMatch>
|
||||
</IfModule>
|
||||
|
|
|
@ -760,7 +760,6 @@ class crud
|
|||
$this->user->log("Service removed");
|
||||
}
|
||||
|
||||
|
||||
public function edit_service($id, $date, $code, $beginning, $end, $chief, $drivers, $crew, $place, $notes, $type, $increment, $inserted_by)
|
||||
{
|
||||
$this->remove_service($id);
|
||||
|
@ -936,7 +935,7 @@ function init_class($enableDebugger=true, $headers=true)
|
|||
$csp_rules[] = "report-uri ".SENTRY_CSP_REPORT_URI;
|
||||
}
|
||||
$csp = implode("; ", $csp_rules);
|
||||
if(!isset($_COOKIE["JSless"]) && (isset($_GET["JSless"]) ? !$_GET["JSless"] : true)){
|
||||
if((isset($_GET["JSless"]) ? !$_GET["JSless"] : true) && !strpos($_SERVER["PHP_SELF"], "offline.php")){
|
||||
header("Content-Security-Policy: $csp");
|
||||
header("X-XSS-Protection: 1; mode=block");
|
||||
header("X-Content-Type-Options: nosniff");
|
||||
|
|
|
@ -129,7 +129,7 @@ $(document).ajaxError(function (event, xhr, settings, error) {
|
|||
});
|
||||
|
||||
if (getCookie("authenticated")) {
|
||||
var installServiceWorker = false;
|
||||
var installServiceWorker = true;
|
||||
if (window.skipServiceWorkerInstallation !== undefined) { // if you want to disable SW for example via GreasyFork userscript
|
||||
installServiceWorker = false;
|
||||
}
|
||||
|
|
|
@ -1,106 +1,74 @@
|
|||
const cacheVersion = process.env.BUNDLE_DATE;
|
||||
const cacheName = "static-" + cacheVersion;
|
||||
const expectedCaches = [cacheName, "tables-1"];
|
||||
//Code taken from https://googlechrome.github.io/samples/service-worker/custom-offline-page/ (and edited)
|
||||
|
||||
const urls = ["offline.php", "manifest.webmanifest", "resources/images/favicon.ico", "resources/dist/marker-icon.png", "resources/dist/layers.png", "resources/dist/layers-2x.png", "resources/images/android-chrome-192x192.png", "resources/images/android-chrome-384x384.png", "resources/images/black_helmet.png", "resources/images/red_helmet.png", "resources/images/wheel.png", "resources/images/logo.png", "resources/images/owner.png", "resources/dist/fonts/fontawesome-webfont.woff2"];
|
||||
const CACHE_NAME = 'offline';
|
||||
const RESOURCES = ["offline.php", "manifest.webmanifest"];
|
||||
|
||||
function fetchHandler (event, contentType, notFoundMessage) {
|
||||
// TODO: refactoring
|
||||
self.addEventListener('install', (event) => {
|
||||
event.waitUntil((async () => {
|
||||
const cache = await caches.open(CACHE_NAME);
|
||||
for(let resource of RESOURCES){
|
||||
console.log(resource);
|
||||
await cache.add(new Request(resource, {cache: 'reload'}));
|
||||
}
|
||||
await fetch("resources/dist/assets-manifest.json")
|
||||
.then((response) => response.json())
|
||||
.then((manifest) => {
|
||||
console.log(manifest);
|
||||
const scriptsRequired = ["main.js", "src_table_engine_default_js.bundle.js"];
|
||||
scriptsRequired.map((scriptName) => {
|
||||
console.log(scriptName);
|
||||
cache.add(new Request("resources/dist/" + manifest[scriptName]["src"], {cache: 'reload'}));
|
||||
});
|
||||
});
|
||||
})());
|
||||
});
|
||||
|
||||
self.addEventListener('activate', (event) => {
|
||||
event.waitUntil((async () => {
|
||||
// Enable navigation preload if it's supported.
|
||||
// See https://developers.google.com/web/updates/2017/02/navigation-preload
|
||||
if ('navigationPreload' in self.registration) {
|
||||
await self.registration.navigationPreload.enable();
|
||||
}
|
||||
})());
|
||||
|
||||
// Tell the active service worker to take control of the page immediately.
|
||||
self.clients.claim();
|
||||
});
|
||||
|
||||
self.addEventListener('fetch', (event) => {
|
||||
console.log(event);
|
||||
|
||||
// FROM https://googlechrome.github.io/samples/service-worker/custom-offline-page/
|
||||
// We only want to call event.respondWith() if this is a navigation request
|
||||
// for an HTML page.
|
||||
if (event.request.mode === "navigate") {
|
||||
event.respondWith((async () => {
|
||||
console.log("respond with");
|
||||
try {
|
||||
// First, try to use the navigation preload response if it's supported.
|
||||
const preloadResponse = await event.preloadResponse;
|
||||
if (preloadResponse) {
|
||||
return preloadResponse;
|
||||
}
|
||||
|
||||
const networkResponse = await fetch(event.request);
|
||||
console.log("network response");
|
||||
return networkResponse;
|
||||
} catch (error) {
|
||||
// catch is only triggered if an exception is thrown, which is likely
|
||||
// due to a network error.
|
||||
// If fetch() returns a valid HTTP response with a response code in
|
||||
// the 4xx or 5xx range, the catch() will NOT be called.
|
||||
console.log("Fetch failed; returning offline page instead.", error);
|
||||
|
||||
const cache = await caches.open(cacheName);
|
||||
if (event.request.headers.get("Accept").includes("text/html")) {
|
||||
cacheFileName = "offline.php";
|
||||
} else {
|
||||
cacheFileName = event.request.url;
|
||||
}
|
||||
const cachedResponse = await cache.match(cacheFileName);
|
||||
return cachedResponse;
|
||||
event.respondWith((async () => {
|
||||
try {
|
||||
// First, try to use the navigation preload response if it's supported.
|
||||
const preloadResponse = await event.preloadResponse;
|
||||
if (preloadResponse) {
|
||||
return preloadResponse;
|
||||
}
|
||||
})());
|
||||
}
|
||||
|
||||
const networkResponse = await fetch(event.request);
|
||||
return networkResponse;
|
||||
} catch (error) {
|
||||
// catch is only triggered if an exception is thrown, which is likely
|
||||
// due to a network error.
|
||||
// If fetch() returns a valid HTTP response with a response code in
|
||||
// the 4xx or 5xx range, the catch() will NOT be called.
|
||||
console.log('Fetch failed; returning offline page instead.', error);
|
||||
|
||||
const cache = await caches.open(CACHE_NAME);
|
||||
let cache_element_name = event.request.url;
|
||||
if (event.request.mode === "navigate") {
|
||||
cache_element_name = "offline.php";
|
||||
}
|
||||
console.log('Cache element name:', cache_element_name);
|
||||
const cachedResponse = await cache.match(cache_element_name);
|
||||
return cachedResponse;
|
||||
}
|
||||
})());
|
||||
|
||||
// If our if() condition is false, then this fetch handler won't intercept the
|
||||
// request. If there are any other fetch handlers registered, they will get a
|
||||
// chance to call event.respondWith(). If no fetch handlers call
|
||||
// event.respondWith(), the request will be handled by the browser as if there
|
||||
// were no service worker involvement.
|
||||
}
|
||||
|
||||
self.addEventListener("fetch", function (event) {
|
||||
const request = event.request;
|
||||
|
||||
// https://stackoverflow.com/a/49719964
|
||||
if (event.request.cache === "only-if-cached" && event.request.mode !== "same-origin") return;
|
||||
|
||||
if (request.headers.get("Accept").includes("text/html")) {
|
||||
fetchHandler(event, null, "offline.php");
|
||||
} else if (request.destination === "script") {
|
||||
fetchHandler(event, "application/javascript", "console.error('Script " + event.request.url + " not found');");
|
||||
} else if (request.destination === "image") {
|
||||
fetchHandler(event, null, "resources/images/logo.png");
|
||||
} else if (request.destination === "font") {
|
||||
fetchHandler(event, null, null);
|
||||
} else if (request.destination === "manifest" || request.url.includes("manifest")) {
|
||||
fetchHandler(event, null, "manifest.webmanifest");
|
||||
} else {
|
||||
event.respondWith(fetch(request));
|
||||
}
|
||||
});
|
||||
|
||||
self.addEventListener("install", (event) => {
|
||||
self.skipWaiting();
|
||||
event.waitUntil(
|
||||
caches.open(cacheName).then((cache) => {
|
||||
cache.addAll(urls);
|
||||
fetch("resources/dist/assets-manifest.json")
|
||||
.then((response) => response.json())
|
||||
.then((manifest) => {
|
||||
const scriptsRequired = ["main.js", "maps.js"];
|
||||
scriptsRequired.map((scriptName) => {
|
||||
console.log(manifest);
|
||||
console.log(scriptName);
|
||||
cache.add("resources/dist/" + manifest[scriptName]["src"]);
|
||||
});
|
||||
});
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
self.addEventListener("activate", (event) => {
|
||||
event.waitUntil(
|
||||
caches.keys().then((keys) => Promise.all(
|
||||
keys.map((key) => {
|
||||
if (!expectedCaches.includes(key)) {
|
||||
console.log("Deleting cache " + key);
|
||||
return caches.delete(key);
|
||||
}
|
||||
})
|
||||
)).then(() => {
|
||||
console.log("Service worker now ready to handle fetches!");
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
|
@ -95,7 +95,8 @@ module.exports = {
|
|||
integrity: true,
|
||||
customize(entry) {
|
||||
allowed_entries = ["main.js", "maps.js", "players.js", "games.js"]
|
||||
if (entry.key.startsWith('fonts') || allowed_entries.includes(entry.key)) {
|
||||
if (entry.key.startsWith('fonts') || entry.key.includes("table_engine") || allowed_entries.includes(entry.key)) {
|
||||
entry.key = entry.key.split("?")[0];
|
||||
return entry;
|
||||
}
|
||||
return false;
|
||||
|
|
|
@ -1 +1 @@
|
|||
{% extends "list.html" %}
|
||||
If you are using an old browser that doesn't support JS, you can't use Service Workers, so this page is useless.
|
|
@ -48,7 +48,7 @@
|
|||
</script>
|
||||
{% endif %}
|
||||
{{ script('main.js') }}
|
||||
<script nonce="{{ nonce }}">$.fn.loading.defaults.message = "{{ 'Loading...'|t }}";</script>
|
||||
<script{% if not is_offline_page %} nonce="{{ nonce }}"{% endif %}>$.fn.loading.defaults.message = "{{ 'Loading...'|t }}";</script>
|
||||
{% endblock %}
|
||||
{% if enable_debug_bar %}{{ debug_bar_head|raw }}{% endif %}
|
||||
</head>
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block menu %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<br>
|
||||
<br>
|
||||
<img alt="VVF" src="./resources/images/owner.png" width="150" style="display: block; margin-left: auto; margin-right: auto;">
|
||||
<br>
|
||||
<br>
|
||||
<div id="list" class="table-responsive">
|
||||
<br>
|
||||
<img alt="VVF" src="./resources/images/owner.png" width="150"
|
||||
style="display: block; margin-left: auto; margin-right: auto;">
|
||||
<br>
|
||||
<br>
|
||||
<div style="display: none" id="list" class="table-responsive">
|
||||
<table id="table" class="table table-striped table-bordered dt-responsive nowrap">
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -18,17 +21,46 @@
|
|||
<th>{{ 'Write'|t }}</th>
|
||||
<th>{{ 'Services'|t }}</th>
|
||||
<th>{{ 'Availability Minutes'|t }}</th>
|
||||
<th>{{ 'Other'|t }}</th>
|
||||
{# <th>{{ 'Other'|t }}</th> TODO: fix "Other" page #}
|
||||
{% endif %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="table_body">
|
||||
</tbody>
|
||||
</table>
|
||||
<script nonce="{{ nonce }}">
|
||||
allertaJS.main.loadTable("list", true, 20000, true);
|
||||
</script>
|
||||
</div>
|
||||
<br>
|
||||
<br>
|
||||
|
||||
<br><br>
|
||||
|
||||
<div class="d-flex justify-content-center">
|
||||
<button style="display: none; margin-left: 5%;" id="delete_cache_btn" type="button" class="btn btn-danger">{{ 'Delete the offline version of Allerta-VVF from this device.'|t }}</button>
|
||||
</div>
|
||||
<div class="d-flex justify-content-center">
|
||||
<div style="display: none" id="cache_empty_msg" class="alert alert-danger" role="alert">
|
||||
<p>{{ 'You have cleared the cache; the table will be loaded when the device is connected to the Internet.'|t }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
allertaJS.main.loadTable({tablePage: "list", useCustomTableEngine: "default", callback: () => {
|
||||
console.log("Callback executed");
|
||||
document.querySelector("#list").style.display = 'block';
|
||||
document.querySelector("#delete_cache_btn").style.display = 'block';
|
||||
document.querySelector("#cache_empty_msg").style.display = 'none';
|
||||
allertaJS.main.loadListListCallback();
|
||||
}});
|
||||
document.addEventListener("DOMContentLoaded", async () => {
|
||||
if(await caches.has("tables-1")){
|
||||
document.querySelector("#list").style.display = 'block';
|
||||
document.querySelector("#delete_cache_btn").style.display = 'block';
|
||||
} else {
|
||||
document.querySelector("#cache_empty_msg").style.display = 'block';
|
||||
}
|
||||
});
|
||||
|
||||
document.querySelector("#delete_cache_btn").addEventListener("click", async () => {
|
||||
await caches.delete("tables-1");
|
||||
location.reload();
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
|
@ -130,5 +130,7 @@ return [
|
|||
"You are not authorized to perform this action." => "You are not authorized to perform this action.",
|
||||
"Bad request." => "Bad request.",
|
||||
"User not exists." => "User not exists.",
|
||||
"Change" => "Change"
|
||||
"Change" => "Change",
|
||||
"Delete the offline version of Allerta-VVF from this device." => "Delete the offline version of Allerta-VVF from this device.",
|
||||
"You have cleared the cache; the table will be loaded when the device is connected to the Internet." => "You have cleared the cache; the table will be loaded when the device is connected to the Internet."
|
||||
];
|
||||
|
|
|
@ -130,5 +130,7 @@ return [
|
|||
"You are not authorized to perform this action." => "Non sei autorizzato ad eseguire questa azione.",
|
||||
"Bad request." => "Errore nella richiesta.",
|
||||
"User not exists." => "L'utente non esiste.",
|
||||
"Change" => "Cambia"
|
||||
"Change" => "Cambia",
|
||||
"Delete the offline version of Allerta-VVF from this device." => "Cancella la versione offline di Allerta-VVF da questo dispositivo.",
|
||||
"You have cleared the cache; the table will be loaded when the device is connected to the Internet." => "Hai svuotato la cache; la tabella verrà caricata quando il dispositivo sarà connesso ad Internet."
|
||||
];
|
||||
|
|
|
@ -97,15 +97,16 @@ $function_resource = new \Twig\TwigFunction(
|
|||
$twig->addFunction($function_resource);
|
||||
|
||||
$function_script = new \Twig\TwigFunction(
|
||||
'script', function ($file) {
|
||||
'script', function ($context, $file) {
|
||||
global $nonce, $url_software, $webpack_manifest;
|
||||
$script_url = $url_software . "/resources/dist/" . $webpack_manifest[$file]["src"];
|
||||
$script_integrity = $webpack_manifest[$file]["integrity"];
|
||||
|
||||
$script_tag = "<script src='{$script_url}' integrity='{$script_integrity}' crossorigin='anonymous' nonce='".$nonce."'";
|
||||
$script_tag = "<script src='{$script_url}'";
|
||||
if(!$context["is_offline_page"]) $script_tag .= " integrity='{$script_integrity}' crossorigin='anonymous' nonce='".$nonce."'";
|
||||
$script_tag .= "></script>";
|
||||
return $script_tag;
|
||||
}, ['is_safe' => ['html']]
|
||||
}, ['needs_context' => true, 'is_safe' => ['html']]
|
||||
);
|
||||
$twig->addFunction($function_script);
|
||||
|
||||
|
@ -157,6 +158,7 @@ function loadtemplate($templatename, $data, $requirelogin=true)
|
|||
$data['user'] = $user->info();
|
||||
$data['show_menu'] = !isset($_REQUEST["hide_menu"]);
|
||||
$data['show_footer'] = !isset($_REQUEST["hide_footer"]);
|
||||
$data['is_offline_page'] = strpos($_SERVER["PHP_SELF"], "offline.php") !== false;
|
||||
if(get_option("use_custom_error_sound")) {
|
||||
$data['error_sound'] = "custom-error.mp3";
|
||||
} else {
|
||||
|
|
Loading…
Reference in New Issue