diff --git a/package.json b/package.json index 7a37613..339ef7b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ludo", - "version": "v1.2.3", + "version": "v1.3.2", "private": true, "scripts": { "start": "yarn serve", @@ -17,6 +17,7 @@ "mersenne-twister": "1.1.0", "register-service-worker": "1.6.2", "sass-rem": "2.0.1", + "toastify-js": "https://github.com/mort3za/toastify-js.git", "vue": "2.6.10", "vue-class-component": "7.0.2", "vue-property-decorator": "8.1.0", diff --git a/src/App.vue b/src/App.vue index 981d760..2ff1754 100644 --- a/src/App.vue +++ b/src/App.vue @@ -16,13 +16,13 @@ body, diff --git a/src/functions/general-helpers.ts b/src/functions/general-helpers.ts index 380a111..6037d25 100644 --- a/src/functions/general-helpers.ts +++ b/src/functions/general-helpers.ts @@ -1,3 +1,6 @@ +// @ts-ignore +import Toastify from "toastify-js"; + import { PositionInBoard, Marble, @@ -12,6 +15,27 @@ import { import { getPositionAfterMove } from "@/functions/path-helpers.ts"; import store from "@/store/index"; +export function notify(options: any = {}) { + if (!options.text) { + console.log('No text'); + return; + } + Toastify({ + className: "shadow", + text: "", + duration: 600000, + // destination: undefined, + // newWindow: false, + // onClick: () => {} + close: false, + gravity: "bottom", // `top` or `bottom` + position: "left", // `left`, `center` or `right` + backgroundColor: "white", + stopOnFocus: true, + ...options + }).showToast(); +} + // TODO: rename to isSamePosition export function isSameStep(position1: PositionInBoard, position2: PositionInBoard): boolean { return position1.row === position2.row && position1.column === position2.column; diff --git a/src/registerServiceWorker.ts b/src/registerServiceWorker.ts index ef87577..1f6fe7e 100644 --- a/src/registerServiceWorker.ts +++ b/src/registerServiceWorker.ts @@ -1,32 +1,55 @@ /* tslint:disable:no-console */ -import { register } from 'register-service-worker'; +import { register } from "register-service-worker"; +import { notify } from "./functions/general-helpers"; -if (process.env.NODE_ENV === 'production') { +const notifyUserAboutUpdate = (worker: any) => { + notify({ + text: "New version available 🥳, Click here to run the new version!", + close: false, + onClick: () => { + console.log("upgrade confirmed"); + worker.postMessage({ action: "skipWaiting" }); + } + }); +}; + +if (process.env.NODE_ENV === "production") { register(`${process.env.BASE_URL}service-worker.js`, { ready() { console.log( - 'App is being served from cache by a service worker.\n' + - 'For more details, visit https://goo.gl/AFskqB', + "App is being served from cache by a service worker.\n" + + "For more details, visit https://goo.gl/AFskqB" ); }, registered() { - console.log('Service worker has been registered.'); + console.log("Service worker has been registered."); }, cached() { - console.log('Content has been cached for offline use.'); + console.log("Content has been cached for offline use."); }, updatefound() { - console.log('New content is downloading.'); + console.log("New content is downloading."); }, - updated() { - console.log('New content is available; please refresh.'); + updated(registration) { + console.log("New content is available; please refresh."); + notifyUserAboutUpdate(registration.waiting); }, offline() { - console.log('No internet connection found. App is running in offline mode.'); + console.log("No internet connection found. App is running in offline mode."); }, error(error) { - console.error('Error during service worker registration:', error); - }, + console.error("Error during service worker registration:", error); + } + }); + + let refreshing: any; + navigator.serviceWorker.addEventListener("controllerchange", () => { + console.log('controllerchange...'); + if (refreshing) { + return; + } + window.location.reload(); + refreshing = true; }); } diff --git a/src/service-worker.js b/src/service-worker.js new file mode 100644 index 0000000..e2faa7e --- /dev/null +++ b/src/service-worker.js @@ -0,0 +1,15 @@ +/** + * The workboxSW.precacheAndRoute() method efficiently caches and responds to + * requests for URLs in the manifest. + * See https://goo.gl/S9QRab + */ +self.__precacheManifest = [].concat(self.__precacheManifest || []); +workbox.precaching.suppressWarnings(); +workbox.precaching.precacheAndRoute(self.__precacheManifest, {}); + +self.addEventListener("message", msg => { + if (msg.data.action === "skipWaiting") { + console.log('skip waiting...'); + self.skipWaiting(); + } +}); diff --git a/src/styles/global.scss b/src/styles/global.scss index a9fde48..4b884a8 100644 --- a/src/styles/global.scss +++ b/src/styles/global.scss @@ -1,3 +1,4 @@ +@import "./toastify.scss"; @import "./global-ghost.scss"; @import "./bootstrap.scss"; @import "./animations.scss"; diff --git a/src/styles/toastify.scss b/src/styles/toastify.scss new file mode 100644 index 0000000..05adb3b --- /dev/null +++ b/src/styles/toastify.scss @@ -0,0 +1,82 @@ +/*! + * Toastify js 1.6.1 + * https://github.com/apvarun/toastify-js + * @license MIT licensed + * + * Copyright (C) 2018 Varun A P + */ + +.toastify { + padding: rem(12px 20px); + color: $dark; + display: inline-block; + background: white; + position: fixed; + opacity: 0; + transition: all 300ms cubic-bezier(0.215, 0.61, 0.355, 1); + border-radius: $border-radius; + cursor: pointer; + text-decoration: none; + max-width: calc(50% - 20px); + z-index: 5000; +} + +.toastify.on { + opacity: 1; +} + +.toast-close { + opacity: 0.4; +} + +.toastify-right { + right: rem(16px); + .toast-close { + padding: rem(0 0 0 16px); + } +} + +.toastify-left { + left: rem(16px); + .toast-close { + padding: rem(0 16px 0 0); + } +} + +.toastify-top { + top: rem(-150px); +} + +.toastify-bottom { + bottom: rem(-150px); +} + +// .toastify-rounded { +// border-radius: 25px; +// } + +// .toastify-avatar { +// width: 1.5em; +// height: 1.5em; +// margin: 0 5px; +// border-radius: 2px; +// } + +.toastify-center { + margin-left: auto; + margin-right: auto; + left: 0; + right: 0; + max-width: fit-content; +} + +@media only screen and (max-width: 360px) { + .toastify-right, + .toastify-left { + margin-left: auto; + margin-right: auto; + left: 0; + right: 0; + max-width: fit-content; + } +} diff --git a/tsconfig.json b/tsconfig.json index d247025..52cfe29 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -37,4 +37,4 @@ "exclude": [ "node_modules" ] -} +} \ No newline at end of file diff --git a/vue.config.js b/vue.config.js index 243cb38..513a6cc 100644 --- a/vue.config.js +++ b/vue.config.js @@ -5,6 +5,13 @@ module.exports = { productionSourceMap: false, publicPath: process.env.NODE_ENV === "production" ? "/ludo/" : "/", pwa: { + // service worker guides: + // https://levelup.gitconnected.com/vue-pwa-example-298a8ea953c9 + // https://redfin.engineering/how-to-fix-the-refresh-button-when-using-service-workers-a8e27af6df68 + workboxPluginMode: 'InjectManifest', + workboxOptions: { + swSrc: 'src/service-worker.js' + }, name: "Ludo Game", themeColor: "#00ffbb", msTileColor: "#2c3939", diff --git a/yarn.lock b/yarn.lock index 1cd9da7..242719c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8702,6 +8702,10 @@ to-regex@^3.0.1, to-regex@^3.0.2: regex-not "^1.0.2" safe-regex "^1.1.0" +"toastify-js@https://github.com/mort3za/toastify-js.git": + version "1.6.1" + resolved "https://github.com/mort3za/toastify-js.git#39974891c867e58bf476004bd8f31f25d209a477" + toidentifier@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" @@ -8872,10 +8876,10 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= -typescript@3.4.3: - version "3.4.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.4.3.tgz#0eb320e4ace9b10eadf5bc6103286b0f8b7c224f" - integrity sha512-FFgHdPt4T/duxx6Ndf7hwgMZZjZpB+U0nMNGVCYPq0rEzWKjEDobm4J6yb3CS7naZ0yURFqdw9Gwc7UOh/P9oQ== +typescript@3.5.3: + version "3.5.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.3.tgz#c830f657f93f1ea846819e929092f5fe5983e977" + integrity sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g== uglify-js@3.4.x: version "3.4.10"