Moved some files around, improved how notifications are being requested, and improved activation and installation-flow of serviceworker

This commit is contained in:
Kasper Rynning-Tønnesen
2020-02-21 14:36:27 +01:00
parent ac1e73ee09
commit 8b1d86bd9b
8 changed files with 291 additions and 113 deletions

View File

@@ -9,7 +9,7 @@ const ServiceWorkerConfig = {
extensions: [".js", ".vue"]
},
entry: {
serviceWorker: [helpers.root("public", "service-worker")]
serviceWorker: [helpers.root("src/service-worker", "service-worker")]
},
optimization: {
minimizer: []
@@ -19,7 +19,7 @@ const ServiceWorkerConfig = {
{
test: /\.js$/,
loader: "babel-loader",
include: [helpers.root("public", "service-worker")]
include: [helpers.root("src/service-worker", "service-worker")]
}
]
},

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24">
<path d="M12 22c1.1 0 2-.9 2-2h-4c0 1.1.89 2 2 2zm6-6v-5c0-3.07-1.64-5.64-4.5-6.32V4c0-.83-.67-1.5-1.5-1.5s-1.5.67-1.5 1.5v.68C7.63 5.36 6 7.92 6 11v5l-2 2v1h16v-1l-2-2z"/>
</svg>

After

Width:  |  Height:  |  Size: 267 B

View File

@@ -2,103 +2,49 @@
<div class="app-container">
<banner />
<router-view />
<UpdateToast
v-if="showToast"
:text="toastText"
:refreshButton="refreshToast"
v-on:closeToast="closeToast"
/>
</div>
</template>
<script>
import ServiceWorkerMixin from "@/mixins/ServiceWorkerMixin";
import banner from "@/ui/Banner";
import UpdateToast from "@/ui/UpdateToast";
export default {
name: "vinlottis",
components: { banner },
components: { banner, UpdateToast },
props: {},
data() {
return {};
return {
showToast: false,
toastText: null,
refreshToast: false
};
},
mounted() {
console.log("SNEAKY PETE!");
if ("serviceWorker" in navigator) {
const channel = new BroadcastChannel("updatePush");
channel.addEventListener("message", event => {
if (event.data.success) {
localStorage.setItem("push", true);
}
this.$on("service-worker-updated", () => {
this.toastText = "Det er ny oppdatering av siden, vil du oppdatere?";
this.showToast = true;
this.refreshToast = true;
});
navigator.serviceWorker
.register("/service-worker.js")
.then(serviceWorker => {
console.log(
"Arbeids arbeideren din er installert. Du kan nå gå offline frem til neste trekning."
);
// From your client pages:
serviceWorker.onupdatefound = () => {
const installingWorker = serviceWorker.installing;
installingWorker.onstatechange = () => {
if (
installingWorker.state === "installed" &&
navigator.serviceWorker.controller
) {
// Preferably, display a message asking the user to reload...
location.reload();
}
};
};
if (!("PushManager" in window)) {
throw new Error("No Push API Support!");
}
window.Notification.requestPermission().then(permission => {
if (permission !== "granted") {
console.log(
"Du valgte å ikke ha arbeids-arbeideren til å sende deg dytte-meldinger :'('"
);
return;
}
if (localStorage.getItem("push") == null) {
this.sendMessage("updatePush");
}
this.$on("push-allowed", () => {
this.toastText = "Push-notifications er skrudd på!";
this.refreshToast = false;
this.showToast = true;
});
})
.catch(error => {
console.error("Arbeids arbeideren klarer ikke arbeide.", error);
});
}
},
computed: {},
mixins: [ServiceWorkerMixin],
methods: {
sendMessage: function(message) {
// This wraps the message posting/response in a promise, which will
// resolve if the response doesn't contain an error, and reject with
// the error if it does. If you'd prefer, it's possible to call
// controller.postMessage() and set up the onmessage handler
// independently of a promise, but this is a convenient wrapper.
return new Promise(function(resolve, reject) {
var messageChannel = new MessageChannel();
messageChannel.port1.onmessage = function(event) {
if (event.data.error) {
reject(event.data.error);
} else {
resolve(event.data);
}
};
// This sends the message data as well as transferring
// messageChannel.port2 to the service worker.
// The service worker can then use the transferred port to reply
// via postMessage(), which will in turn trigger the onmessage
// handler on messageChannel.port1.
// See
// https://html.spec.whatwg.org/multipage/workers.html#dom-worker-postmessage
if (navigator.serviceWorker.controller == null) {
resolve();
}
navigator.serviceWorker.controller.postMessage(message, [
messageChannel.port2
]);
});
closeToast: function() {
this.showToast = false;
}
}
};

View File

@@ -1,7 +1,17 @@
<template>
<div class="outer">
<div class="container">
<div class="header-top">
<h1 class="title" @click="startCountdown">Vinlotteri</h1>
<img
src="/public/assets/images/notification.svg"
alt="Notification-bell"
@click="requestNotificationAccess"
class="notification-request-button"
role="button"
v-if="notificationAllowed"
/>
</div>
<router-link to="generate" class="generate-link">
Klarer du ikke velge lodd-farger?
<span class="subtext generator-link">Prøv loddgeneratoren</span>
@@ -57,10 +67,23 @@ export default {
data() {
return {
hardStart: false,
todayExists: false
todayExists: false,
pushAllowed: false
};
},
computed: {
notificationAllowed: function() {
return (
Notification.permission !== "granted" ||
!this.pushAllowed ||
localStorage.getItem("push") == null
);
}
},
mounted() {
this.$on("push-allowed", () => {
this.pushAllowed = true;
});
fetch("/api/wines/prelottery")
.then(wines => wines.json())
.then(wines => {
@@ -76,6 +99,9 @@ export default {
this.track();
},
methods: {
requestNotificationAccess() {
this.$root.$children[0].registerServiceWorkerPushNotification();
},
changeEnabled(way) {
this.hardStart = way;
},
@@ -93,6 +119,11 @@ export default {
@import "../styles/global.scss";
@import "../styles/media-queries.scss";
.notification-request-button {
cursor: pointer;
margin-left: 15px;
}
.bottom-container {
display: flex;
flex-direction: row;
@@ -107,8 +138,22 @@ export default {
}
}
.title {
.header-top {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
margin-bottom: 2rem;
margin-top: 3.8rem;
@include mobile {
margin-top: 1.5rem;
}
.title {
cursor: pointer;
margin: auto 0;
}
}
.left-bottom {

View File

@@ -0,0 +1,90 @@
var serviceWorkerRegistrationMixin = {
created: function() {
if (!("serviceWorker" in navigator)) {
console.log("Nettleseren din støtter ikke service-workers.");
return;
}
if (Notification.permission !== "granted") {
localStorage.removeItem("push");
}
this.registerPushListener();
this.registerServiceWorker();
},
methods: {
registerPushListener: function() {
const channel = new BroadcastChannel("updatePush");
channel.addEventListener("message", event => {
if (event.data.success) {
localStorage.setItem("push", true);
this.$emit("push-allowed");
}
});
},
sendMessage: function(message) {
return new Promise(function(resolve, reject) {
var messageChannel = new MessageChannel();
messageChannel.port1.onmessage = function(event) {
if (event.data.error) {
reject(event.data.error);
} else {
resolve(event.data);
}
};
if (navigator.serviceWorker.controller == null) {
resolve();
} else {
navigator.serviceWorker.controller.postMessage(message, [
messageChannel.port2
]);
}
});
},
serviceWorkerUpdateFoundListener: function(serviceWorker) {
const installingWorker = serviceWorker.installing;
installingWorker.onstatechange = () => {
if (
installingWorker.state === "installed" &&
navigator.serviceWorker.controller
) {
this.$emit("service-worker-updated");
}
};
},
registerServiceWorkerPushNotification: function() {
if (!("PushManager" in window)) {
throw new Error("No Push API Support!");
}
window.Notification.requestPermission().then(permission => {
if (permission !== "granted") {
console.log(
"Du valgte å ikke ha arbeids-arbeideren til å sende deg dytte-meldinger :'('"
);
return;
}
if (localStorage.getItem("push") == null) {
this.sendMessage("updatePush");
}
});
},
registerServiceWorker: function() {
if ("serviceWorker" in navigator) {
navigator.serviceWorker
.register("/service-worker.js")
.then(serviceWorker => {
console.log(
"Arbeids arbeideren din er installert. Du kan nå gå offline frem til neste trekning."
);
serviceWorker.onupdatefound = () => {
this.serviceWorkerUpdateFoundListener(serviceWorker);
};
//this.registerServiceWorkerPushNotification();
})
.catch(error => {
console.error("Arbeids arbeideren klarer ikke arbeide.", error);
});
}
}
}
};
module.exports = serviceWorkerRegistrationMixin;

View File

@@ -8,30 +8,10 @@ console.log("Nåværende versjon:", version);
self.addEventListener("activate", event => {
console.log("Aktiverer");
event.waitUntil(self.clients.claim());
event.waitUntil(removeCache(CACHE_NAME));
event.waitUntil(removeCache(CACHE_NAME_API));
event.waitUntil(addCache(CACHE_NAME, STATIC_CACHE_URLS));
event.waitUntil(
new Promise((resolve, reject) => {
const applicationServerKey = urlB64ToUint8Array(__PUBLICKEY__);
const options = { applicationServerKey, userVisibleOnly: true };
self.registration.pushManager
.subscribe(options)
.then(subscription =>
saveSubscription(subscription)
.then(() => {
resolve();
})
.catch(() => {
resolve();
})
)
.catch(() => {
console.log("Kunne ikke legge til pushnotifications");
resolve();
});
})
);
});
self.addEventListener("message", event => {
@@ -121,7 +101,7 @@ function showLocalNotification(title, body, swRegistration) {
}
async function saveSubscription(subscription) {
const SERVER_URL = "https://lottis.vin/subscription/save-subscription";
const SERVER_URL = "/subscription/save-subscription";
const response = await fetch(SERVER_URL, {
method: "post",
headers: {

100
src/ui/UpdateToast.vue Normal file
View File

@@ -0,0 +1,100 @@
<template>
<div class="update-toast" :class="showClass">
<span>{{text}}</span>
<div class="button-container">
<button v-if="refreshButton" @click="refresh">Refresh</button>
<button @click="closeToast">Lukk</button>
</div>
</div>
</template>
<script>
export default {
props: {
text: { type: String, required: true },
refreshButton: { type: Boolean, required: false }
},
data() {
return { showClass: null };
},
created() {
this.showClass = "show";
},
mounted() {
if (this.refreshButton) {
return;
}
setTimeout(() => {
this.$emit("closeToast");
}, 5000);
},
methods: {
refresh: function() {
location.reload();
},
closeToast: function() {
this.$emit("closeToast");
}
}
};
</script>
<style lang="scss" scoped>
@import "../styles/media-queries.scss";
.update-toast {
position: fixed;
bottom: 10px;
left: 0;
right: 0;
margin: auto;
background: #2d2d2d;
border-radius: 5px;
padding: 15px;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
width: 50vw;
opacity: 0;
pointer-events: none;
&.show {
pointer-events: all;
opacity: 1;
}
-webkit-transition: opacity 0.5s ease-in-out;
-moz-transition: opacity 0.5s ease-in-out;
-ms-transition: opacity 0.5s ease-in-out;
-o-transition: opacity 0.5s ease-in-out;
transition: opacity 0.5s ease-in-out;
@include mobile {
width: 85vw;
bottom: 0px;
border-bottom-left-radius: 0px;
border-bottom-right-radius: 0px;
}
& span {
color: white;
}
& .button-container {
& button {
color: #2d2d2d;
background-color: white;
border-radius: 5px;
padding: 10px;
margin: 0 3px;
font-size: 0.8rem;
&:active {
background: #2d2d2d;
color: white;
}
}
}
}
</style>

View File

@@ -1,7 +1,12 @@
<template>
<div class="inner-wine-container">
<div class="inner-wine-container" :class="{ 'big': fullscreen }">
<div class="left">
<img :src="wine.image" class="wine-image" :class="{ 'fullscreen': fullscreen }"/>
<!-- <img :src="wine.image" class="wine-image" :class="{ 'fullscreen': fullscreen }"/> -->
<img
src="https://images.vivino.com/thumbs/QRhTyEmKR8Wi_C1N2uqBWg_pb_x960.png"
class="wine-image"
:class="{ 'fullscreen': fullscreen }"
/>
</div>
<div class="right">
<h2>{{ wine.name }}</h2>
@@ -12,7 +17,7 @@
Vunnet av:
{{wine.winners.join(", ")}}
</span>
<div class="color-wins">
<div class="color-wins" :class="{ 'big': fullscreen }">
<span class="color-win blue">{{wine.blue == undefined ? 0 : wine.blue}}</span>
<span class="color-win red">{{wine.red == undefined ? 0 : wine.red}}</span>
<span class="color-win green">{{wine.green == undefined ? 0 : wine.green}}</span>
@@ -45,6 +50,7 @@ export default {
&.fullscreen {
@include desktop {
height: unset;
max-height: 65vh;
}
}
}
@@ -56,6 +62,10 @@ export default {
flex-wrap: wrap;
}
.color-wins.big {
width: unset;
}
span.color-win {
border: 2px solid transparent;
color: #333;
@@ -111,6 +121,10 @@ h3 {
font-family: Arial;
margin-bottom: 30px;
&.big {
align-items: center;
}
@include desktop {
justify-content: center;
}