]> _ Git - tortuga-home.git/commitdiff
.
authorVincent <vincent@enhydra.fr>
Sun, 25 Oct 2020 18:25:34 +0000 (19:25 +0100)
committerVincent <vincent@enhydra.fr>
Sun, 25 Oct 2020 18:25:34 +0000 (19:25 +0100)
.idea/workspace.xml
cache-polyfill.js [new file with mode: 0644]
index.php
js/webapp.js
manifest.php
service-worker.js
service-worker.js.template [deleted file]

index dfb5bbd568d63de66f73378316f1362a6a436468..f78f3201222c5ddaaefebed0545b7778ef51d4b3 100644 (file)
@@ -2,11 +2,13 @@
 <project version="4">
   <component name="ChangeListManager">
     <list default="true" id="352ce63a-b52a-41a2-979b-becda7920939" name="Default" comment=".">
-      <change afterPath="$PROJECT_DIR$/scripts/lib/netflix.php" afterDir="false" />
+      <change afterPath="$PROJECT_DIR$/cache-polyfill.js" afterDir="false" />
       <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/index.php" beforeDir="false" afterPath="$PROJECT_DIR$/index.php" afterDir="false" />
       <change beforePath="$PROJECT_DIR$/js/webapp.js" beforeDir="false" afterPath="$PROJECT_DIR$/js/webapp.js" afterDir="false" />
-      <change beforePath="$PROJECT_DIR$/scripts/lib/scenes.php" beforeDir="false" afterPath="$PROJECT_DIR$/scripts/lib/scenes.php" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/manifest.php" beforeDir="false" afterPath="$PROJECT_DIR$/manifest.php" afterDir="false" />
       <change beforePath="$PROJECT_DIR$/service-worker.js" beforeDir="false" afterPath="$PROJECT_DIR$/service-worker.js" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/service-worker.js.template" beforeDir="false" />
     </list>
     <option name="SHOW_DIALOG" value="false" />
     <option name="HIGHLIGHT_CONFLICTS" value="true" />
@@ -55,8 +57,8 @@
         <option value="Less File" />
         <option value="Setup Script" />
         <option value="Python Script" />
-        <option value="JavaScript File" />
         <option value="HTML File" />
+        <option value="JavaScript File" />
       </list>
     </option>
   </component>
         <option name="Make" enabled="true" />
       </method>
     </configuration>
-    <configuration default="true" type="ArquillianJUnit" factoryName="" nameIsGenerated="true">
-      <option name="arquillianRunConfiguration">
-        <value>
-          <option name="containerStateName" value="" />
-        </value>
-      </option>
-      <option name="TEST_OBJECT" value="class" />
-      <method v="2">
-        <option name="Make" enabled="true" />
-      </method>
-    </configuration>
     <configuration default="true" type="ArquillianTestNG" factoryName="">
       <option name="arquillianRunConfiguration">
         <value>
       <workItem from="1602862886793" duration="1014000" />
       <workItem from="1603095373591" duration="1368000" />
       <workItem from="1603270756890" duration="3366000" />
-      <workItem from="1603558842019" duration="12945000" />
-    </task>
-    <task id="LOCAL-00305" summary=".">
-      <created>1582130758226</created>
-      <option name="number" value="00305" />
-      <option name="presentableId" value="LOCAL-00305" />
-      <option name="project" value="LOCAL" />
-      <updated>1582130758226</updated>
+      <workItem from="1603558842019" duration="15438000" />
+      <workItem from="1603646440968" duration="3050000" />
     </task>
     <task id="LOCAL-00306" summary=".">
       <created>1582220578115</created>
       <option name="project" value="LOCAL" />
       <updated>1603626112636</updated>
     </task>
-    <option name="localTasksCounter" value="354" />
+    <task id="LOCAL-00354" summary=".">
+      <created>1603643895376</created>
+      <option name="number" value="00354" />
+      <option name="presentableId" value="LOCAL-00354" />
+      <option name="project" value="LOCAL" />
+      <updated>1603643895376</updated>
+    </task>
+    <option name="localTasksCounter" value="355" />
     <servers />
   </component>
   <component name="TypeScriptGeneratedFilesManager">
       <screen x="0" y="0" width="2560" height="1040" />
     </state>
     <state x="872" y="161" key="#Plugins/0.0.2560.1040@0.0.2560.1040" timestamp="1596008801840" />
-    <state x="198" y="0" width="745" height="567" key="CommitChangelistDialog2" timestamp="1603626109154">
+    <state x="198" y="0" width="745" height="567" key="CommitChangelistDialog2" timestamp="1603643887312">
       <screen x="0" y="0" width="2560" height="1040" />
     </state>
     <state x="701" y="75" key="CommitChangelistDialog2/0.0.1920.1160@0.0.1920.1160" timestamp="1602171117745" />
-    <state x="198" y="0" width="745" height="567" key="CommitChangelistDialog2/0.0.2560.1040@0.0.2560.1040" timestamp="1603626109154" />
+    <state x="198" y="0" width="745" height="567" key="CommitChangelistDialog2/0.0.2560.1040@0.0.2560.1040" timestamp="1603643887312" />
     <state x="1114" y="443" key="NewPhpFileDialog" timestamp="1603637447680">
       <screen x="0" y="0" width="2560" height="1040" />
     </state>
diff --git a/cache-polyfill.js b/cache-polyfill.js
new file mode 100644 (file)
index 0000000..81981d7
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+  Source: https://github.com/coonsta/cache-polyfill
+  Author: https://github.com/coonsta
+*/
+
+if (!Cache.prototype.add) {
+    Cache.prototype.add = function add(request) {
+        return this.addAll([request]);
+    };
+}
+
+if (!Cache.prototype.addAll) {
+    Cache.prototype.addAll = function addAll(requests) {
+        var cache = this;
+
+        // Since DOMExceptions are not constructable:
+        function NetworkError(message) {
+            this.name = 'NetworkError';
+            this.code = 19;
+            this.message = message;
+        }
+        NetworkError.prototype = Object.create(Error.prototype);
+
+        return Promise.resolve().then(function() {
+            if (arguments.length < 1) throw new TypeError();
+
+            // Simulate sequence<(Request or USVString)> binding:
+            var sequence = [];
+
+            requests = requests.map(function(request) {
+                if (request instanceof Request) {
+                    return request;
+                }
+                else {
+                    return String(request); // may throw TypeError
+                }
+            });
+
+            return Promise.all(
+                requests.map(function(request) {
+                    if (typeof request === 'string') {
+                        request = new Request(request);
+                    }
+
+                    var scheme = new URL(request.url).protocol;
+
+                    if (scheme !== 'http:' && scheme !== 'https:') {
+                        throw new NetworkError("Invalid scheme");
+                    }
+
+                    return fetch(request.clone());
+                })
+            );
+        }).then(function(responses) {
+            // TODO: check that requests don't overwrite one another
+            // (don't think this is possible to polyfill due to opaque responses)
+            return Promise.all(
+                responses.map(function(response, i) {
+                    return cache.put(requests[i], response);
+                })
+            );
+        }).then(function() {
+            return undefined;
+        });
+    };
+}
+
+if (!CacheStorage.prototype.match) {
+    // This is probably vulnerable to race conditions (removing caches etc)
+    CacheStorage.prototype.match = function match(request, opts) {
+        var caches = this;
+
+        return this.keys().then(function(cacheNames) {
+            var match;
+
+            return cacheNames.reduce(function(chain, cacheName) {
+                return chain.then(function() {
+                    return match || caches.open(cacheName).then(function(cache) {
+                        return cache.match(request, opts);
+                    }).then(function(response) {
+                        match = response;
+                        return match;
+                    });
+                });
+            }, Promise.resolve());
+        });
+    };
+}
index 20a86d846338127fc61d86e24812b789e625ff1d..0956f1629ae34ba0f3b3cad8e32963c501a202da 100644 (file)
--- a/index.php
+++ b/index.php
@@ -80,7 +80,7 @@ function relativePath($p)
     </main>
 </div>
 <?php
-if ($android) {
+if (true || $android) {
     echo '<script src="' . relativePath('js/webapp.js') . '"></script>' . "\n";
 } ?>
 <script>
index f3d6047bbdabfe02c68ba2232f54607789ac566b..5d7193d0ff5ff68f0f34fb780b08c3f6ef2b1e2f 100644 (file)
@@ -29,11 +29,34 @@ function installapp() {
     });
 }
 
-
-/* Only register a service worker if it's supported */
-if ('serviceWorker' in navigator) {
-    window.addEventListener('load', function() {
-        console.log('👍', 'navigator.serviceWorker is supported');
-        navigator.serviceWorker.register('/service-worker.js');
+function checkForPageUpdate(registration) {
+    // onupdatefound will fire on first time install and when serviceWorker.js file changes
+    registration.addEventListener("updatefound", function() {
+        // To check if service worker is already installed and controlling the page or not
+        if (navigator.serviceWorker.controller) {
+            var installingSW = registration.installing;
+            installingSW.onstatechange = function() {
+                console.info("Service Worker State :", installingSW.state);
+                switch(installingSW.state) {
+                    case 'installed':
+                        // Now new contents will be added to cache and old contents will be remove so
+                        // this is perfect time to show user that page content is updated.
+                        toast('Site is updated. Refresh the page.', 5000);
+                        break;
+                    case 'redundant':
+                        throw new Error('The installing service worker became redundant.');
+                }
+            }
+        }
     });
 }
+
+if ("serviceWorker" in navigator) {
+    navigator.serviceWorker.register('./service-worker.js', {scope: "./"}) //setting scope of sw
+        .then(function (registration) {
+            console.info('Service worker is registered!');
+            checkForPageUpdate(registration); // To check if new content is updated or not
+        })        .catch(function (error) {
+            console.error('Service worker failed ', error);
+        });
+}
index c33554373ad1c06bc55654ae6e1cc5dabf0169b3..b85923b68ef1f79fc09bcf2814149100fcda0dcf 100644 (file)
@@ -1,4 +1,5 @@
 <?php
+$host = 'https://' . $_SERVER['HTTP_HOST'] . '/';
 header('Content-type: application/manifest+json');
 $manifest = [
     'name' => $_GET['n'],
@@ -8,12 +9,11 @@ $manifest = [
     'orientation' => 'landscape',
     'background_color' => '#0a4a9e',
     'theme_color' => '#0a4a9e',
-    'start_url' => '/',
-    'scope' => '/',
+    'start_url' => './index.php',
     'icons' => [],
     'lang' => 'fr',
     'share_target' => [
-        'action' => 'https://' . $_SERVER['HTTP_HOST'] . '/scripts/share.php',
+        'action' =>  './scripts/share.php',
         'method' => 'POST',
         'enctype' => 'application/x-www-form-urlencoded',
         'params' => [
@@ -26,7 +26,7 @@ $manifest = [
 
 $icons = [36, 48, 72, 96, 144, 192, 256, 512, 1024];
 foreach ($icons as $size) {
-    $manifest['icons'][] = ['src' => '/images/favicon/android-icon-' . $size . 'x' . $size . '.png',
+    $manifest['icons'][] = ['src' => './images/favicon/android-icon-' . $size . 'x' . $size . '.png',
         'sizes' => $size . 'x' . $size,
         'type' => 'image/png',
         'density' => $size / 48];
index 4ad1a6c50997054108f34fa263a85031cb955155..c64118c4f6c0c21227a1e5e4cd0a00d1a925c166 100644 (file)
+//Cache polyfil to support cacheAPI in all browsers
+importScripts('./cache-polyfill.js');
+
+var cacheName = 'cache-v4';
+
+//Files to save in cache
+var files = [
+    './',
+    './index.php', //SW treats query string as new request
+    // 'https://fonts.googleapis.com/css?family=Roboto:200,300,400,500,700', //caching 3rd party content
+    // './css/styles.css',
+    // './images/icons/android-chrome-192x192.png',
+    // './images/push-on.png',
+    // './images/push-off.png',
+    // './images/icons/favicon-16x16.png',
+    // './images/icons/favicon-32x32.png',
+    // './js/main.js',
+    // './js/app.js',
+    // './js/offline.js',
+    // './js/push.js',
+    // './js/sync.js',
+    // './js/toast.js',
+    // './js/share.js',
+    // './js/menu.js',
+    // './manifest.json'
+];
+
+//Adding `install` event listener
 self.addEventListener('install', (event) => {
-    console.log('👷', 'install', event);
-    self.skipWaiting();
+    console.info('Event: Install');
+
+    event.waitUntil(
+        caches.open(cacheName)
+            .then((cache) => {
+                //[] of files to cache & if any of the file not present `addAll` will fail
+                return cache.addAll(files)
+                    .then(() => {
+                        console.info('All files are cached');
+                        return self.skipWaiting(); //To forces the waiting service worker to become the active service worker
+                    })
+                    .catch((error) =>  {
+                        console.error('Failed to cache', error);
+                    })
+            })
+    );
 });
 
-self.addEventListener('activate', (event) => {
-    console.log('👷', 'activate', event);
-    return self.clients.claim();
+/*
+  FETCH EVENT: triggered for every request made by index page, after install.
+*/
+
+//Adding `fetch` event listener
+self.addEventListener('fetch', (event) => {
+    console.info('Event: Fetch');
+
+    var request = event.request;
+    var url = new URL(request.url);
+    if (url.origin === location.origin) {
+        // Static files cache
+        event.respondWith(cacheFirst(request));
+    } else {
+        // Dynamic API cache
+        event.respondWith(networkFirst(request));
+    }
+
+    // // Checking for navigation preload response
+    // if (event.preloadResponse) {
+    //   console.info('Using navigation preload');
+    //   return response;
+    // }
 });
 
-// self.addEventListener('fetch', function(event) {
-//     // console.log('👷', 'fetch', event);
-//     event.respondWith(fetch(event.request));
-// });
\ No newline at end of file
+async function cacheFirst(request) {
+    const cachedResponse = await caches.match(request);
+    return cachedResponse || fetch(request);
+}
+
+async function networkFirst(request) {
+    const dynamicCache = await caches.open(cacheName);
+    try {
+        const networkResponse = await fetch(request);
+        // Cache the dynamic API response
+        dynamicCache.put(request, networkResponse.clone()).catch((err) => {
+            console.warn(request.url + ': ' + err.message);
+        });
+        return networkResponse;
+    } catch (err) {
+        const cachedResponse = await dynamicCache.match(request);
+        return cachedResponse;
+    }
+}
+
+/*
+  ACTIVATE EVENT: triggered once after registering, also used to clean up caches.
+*/
+
+//Adding `activate` event listener
+self.addEventListener('activate', (event) => {
+    console.info('Event: Activate');
+
+    //Navigation preload is help us make parallel request while service worker is booting up.
+    //Enable - chrome://flags/#enable-service-worker-navigation-preload
+    //Support - Chrome 57 beta (behing the flag)
+    //More info - https://developers.google.com/web/updates/2017/02/navigation-preload#the-problem
+
+    // Check if navigationPreload is supported or not
+    // if (self.registration.navigationPreload) {
+    //   self.registration.navigationPreload.enable();
+    // }
+    // else if (!self.registration.navigationPreload) {
+    //   console.info('Your browser does not support navigation preload.');
+    // }
+
+    //Remove old and unwanted caches
+    event.waitUntil(
+        caches.keys().then((cacheNames) => {
+            return Promise.all(
+                cacheNames.map((cache) => {
+                    if (cache !== cacheName) {
+                        return caches.delete(cache); //Deleting the old cache (cache v1)
+                    }
+                })
+            );
+        })
+            .then(function () {
+                console.info("Old caches are cleared!");
+                // To tell the service worker to activate current one
+                // instead of waiting for the old one to finish.
+                return self.clients.claim();
+            })
+    );
+});
diff --git a/service-worker.js.template b/service-worker.js.template
deleted file mode 100644 (file)
index 8e0807f..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-const CACHE_NAME = 'mpwa-cache-v1';
-const urlsToCache = $cached;
-
-// Listen for the install event, which fires when the service worker is installing
-self.addEventListener('install', event => {
-    // Ensures the install event doesn't complete until after the cache promise resolves
-    // This is so we don't move on to other events until the critical initial cache is done
-    event.waitUntil(
-        // Open a named cache, then add all the specified URLs to it
-        caches.open(CACHE_NAME).then(cache => cache.addAll(urlsToCache))
-    );
-});
-
-// Listen for the activate event, which is fired after installation
-// Activate is when the service worker actually takes over from the previous
-// version, which is a good time to clean up old caches
-self.addEventListener('activate', event => {
-    console.log('Finally active. Ready to serve!');
-    event.waitUntil(
-        // Get the keys of all the old caches
-        caches
-            .keys()
-            // Ensure we don't resolve until all the promises do (i.e. each key has been deleted)
-            .then(keys =>
-                Promise.all(
-                    keys
-                        // Remove any cache that matches the current cache name
-                        .filter(key => key !== CACHE_NAME)
-                        // Map over the array of old cache names and delete them all
-                        .map(key => caches.delete(key))
-                )
-            )
-    );
-});
-
-// Listen for browser fetch events. These fire any time the browser tries to load
-// any outside resources
-self.addEventListener('fetch', function(event) {
-    // This lets us control the response
-    // We pass in a promise that resolves with a response object
-    event.respondWith(
-        // Check whether we have a matching response for this request in our cache
-        caches.match(event.request).then(response => {
-            // It's in the cache! Serve the response straight from there
-            if (response) {
-                console.log('Serving response from the cache');
-                return response;
-            }
-            // If it's not in the cache we make a fetch request for the resource
-            return (
-                fetch(event.request)
-                    // Then we open our cache
-                    .then(response => caches.open(CACHE_NAME))
-                    // Then we put the request into the cache, so we have it offline next time
-                    .then(cache => {
-                        // We have to clone the response as response streams can only be read once
-                        // This way we can put one copy in the cache and return the other to the browser
-                        cache.put(event.request, response.clone());
-                        return response;
-                    })
-                    .catch(response => {
-                        console.log('Fetch failed, sorry.');
-                    })
-            );
-        })
-    );
-});