Moved some files around, improved how notifications are being requested, and improved activation and installation-flow of serviceworker
This commit is contained in:
@@ -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")]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
3
public/assets/images/notification.svg
Normal file
3
public/assets/images/notification.svg
Normal 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 |
@@ -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;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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 {
|
||||
|
||||
90
src/mixins/serviceWorkerMixin.js
Normal file
90
src/mixins/serviceWorkerMixin.js
Normal 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;
|
||||
@@ -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
100
src/ui/UpdateToast.vue
Normal 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>
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user