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"]
|
extensions: [".js", ".vue"]
|
||||||
},
|
},
|
||||||
entry: {
|
entry: {
|
||||||
serviceWorker: [helpers.root("public", "service-worker")]
|
serviceWorker: [helpers.root("src/service-worker", "service-worker")]
|
||||||
},
|
},
|
||||||
optimization: {
|
optimization: {
|
||||||
minimizer: []
|
minimizer: []
|
||||||
@@ -19,7 +19,7 @@ const ServiceWorkerConfig = {
|
|||||||
{
|
{
|
||||||
test: /\.js$/,
|
test: /\.js$/,
|
||||||
loader: "babel-loader",
|
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">
|
<div class="app-container">
|
||||||
<banner />
|
<banner />
|
||||||
<router-view />
|
<router-view />
|
||||||
|
<UpdateToast
|
||||||
|
v-if="showToast"
|
||||||
|
:text="toastText"
|
||||||
|
:refreshButton="refreshToast"
|
||||||
|
v-on:closeToast="closeToast"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import ServiceWorkerMixin from "@/mixins/ServiceWorkerMixin";
|
||||||
import banner from "@/ui/Banner";
|
import banner from "@/ui/Banner";
|
||||||
|
import UpdateToast from "@/ui/UpdateToast";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "vinlottis",
|
name: "vinlottis",
|
||||||
components: { banner },
|
components: { banner, UpdateToast },
|
||||||
props: {},
|
props: {},
|
||||||
data() {
|
data() {
|
||||||
return {};
|
return {
|
||||||
|
showToast: false,
|
||||||
|
toastText: null,
|
||||||
|
refreshToast: false
|
||||||
|
};
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
console.log("SNEAKY PETE!");
|
console.log("SNEAKY PETE!");
|
||||||
if ("serviceWorker" in navigator) {
|
this.$on("service-worker-updated", () => {
|
||||||
const channel = new BroadcastChannel("updatePush");
|
this.toastText = "Det er ny oppdatering av siden, vil du oppdatere?";
|
||||||
channel.addEventListener("message", event => {
|
this.showToast = true;
|
||||||
if (event.data.success) {
|
this.refreshToast = true;
|
||||||
localStorage.setItem("push", true);
|
});
|
||||||
}
|
this.$on("push-allowed", () => {
|
||||||
});
|
this.toastText = "Push-notifications er skrudd på!";
|
||||||
|
this.refreshToast = false;
|
||||||
navigator.serviceWorker
|
this.showToast = true;
|
||||||
.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");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error("Arbeids arbeideren klarer ikke arbeide.", error);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
computed: {},
|
computed: {},
|
||||||
|
mixins: [ServiceWorkerMixin],
|
||||||
methods: {
|
methods: {
|
||||||
sendMessage: function(message) {
|
closeToast: function() {
|
||||||
// This wraps the message posting/response in a promise, which will
|
this.showToast = false;
|
||||||
// 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
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="outer">
|
<div class="outer">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h1 class="title" @click="startCountdown">Vinlotteri</h1>
|
<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">
|
<router-link to="generate" class="generate-link">
|
||||||
Klarer du ikke velge lodd-farger?
|
Klarer du ikke velge lodd-farger?
|
||||||
<span class="subtext generator-link">Prøv loddgeneratoren</span>
|
<span class="subtext generator-link">Prøv loddgeneratoren</span>
|
||||||
@@ -57,10 +67,23 @@ export default {
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
hardStart: false,
|
hardStart: false,
|
||||||
todayExists: false
|
todayExists: false,
|
||||||
|
pushAllowed: false
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
notificationAllowed: function() {
|
||||||
|
return (
|
||||||
|
Notification.permission !== "granted" ||
|
||||||
|
!this.pushAllowed ||
|
||||||
|
localStorage.getItem("push") == null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
this.$on("push-allowed", () => {
|
||||||
|
this.pushAllowed = true;
|
||||||
|
});
|
||||||
fetch("/api/wines/prelottery")
|
fetch("/api/wines/prelottery")
|
||||||
.then(wines => wines.json())
|
.then(wines => wines.json())
|
||||||
.then(wines => {
|
.then(wines => {
|
||||||
@@ -76,6 +99,9 @@ export default {
|
|||||||
this.track();
|
this.track();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
requestNotificationAccess() {
|
||||||
|
this.$root.$children[0].registerServiceWorkerPushNotification();
|
||||||
|
},
|
||||||
changeEnabled(way) {
|
changeEnabled(way) {
|
||||||
this.hardStart = way;
|
this.hardStart = way;
|
||||||
},
|
},
|
||||||
@@ -93,6 +119,11 @@ export default {
|
|||||||
@import "../styles/global.scss";
|
@import "../styles/global.scss";
|
||||||
@import "../styles/media-queries.scss";
|
@import "../styles/media-queries.scss";
|
||||||
|
|
||||||
|
.notification-request-button {
|
||||||
|
cursor: pointer;
|
||||||
|
margin-left: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
.bottom-container {
|
.bottom-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
@@ -107,8 +138,22 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.title {
|
.header-top {
|
||||||
cursor: pointer;
|
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 {
|
.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 => {
|
self.addEventListener("activate", event => {
|
||||||
console.log("Aktiverer");
|
console.log("Aktiverer");
|
||||||
|
|
||||||
|
event.waitUntil(self.clients.claim());
|
||||||
event.waitUntil(removeCache(CACHE_NAME));
|
event.waitUntil(removeCache(CACHE_NAME));
|
||||||
event.waitUntil(removeCache(CACHE_NAME_API));
|
event.waitUntil(removeCache(CACHE_NAME_API));
|
||||||
event.waitUntil(addCache(CACHE_NAME, STATIC_CACHE_URLS));
|
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 => {
|
self.addEventListener("message", event => {
|
||||||
@@ -121,7 +101,7 @@ function showLocalNotification(title, body, swRegistration) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function saveSubscription(subscription) {
|
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, {
|
const response = await fetch(SERVER_URL, {
|
||||||
method: "post",
|
method: "post",
|
||||||
headers: {
|
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>
|
<template>
|
||||||
<div class="inner-wine-container">
|
<div class="inner-wine-container" :class="{ 'big': fullscreen }">
|
||||||
<div class="left">
|
<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>
|
||||||
<div class="right">
|
<div class="right">
|
||||||
<h2>{{ wine.name }}</h2>
|
<h2>{{ wine.name }}</h2>
|
||||||
@@ -12,7 +17,7 @@
|
|||||||
Vunnet av:
|
Vunnet av:
|
||||||
{{wine.winners.join(", ")}}
|
{{wine.winners.join(", ")}}
|
||||||
</span>
|
</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 blue">{{wine.blue == undefined ? 0 : wine.blue}}</span>
|
||||||
<span class="color-win red">{{wine.red == undefined ? 0 : wine.red}}</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>
|
<span class="color-win green">{{wine.green == undefined ? 0 : wine.green}}</span>
|
||||||
@@ -45,6 +50,7 @@ export default {
|
|||||||
&.fullscreen {
|
&.fullscreen {
|
||||||
@include desktop {
|
@include desktop {
|
||||||
height: unset;
|
height: unset;
|
||||||
|
max-height: 65vh;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -56,6 +62,10 @@ export default {
|
|||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.color-wins.big {
|
||||||
|
width: unset;
|
||||||
|
}
|
||||||
|
|
||||||
span.color-win {
|
span.color-win {
|
||||||
border: 2px solid transparent;
|
border: 2px solid transparent;
|
||||||
color: #333;
|
color: #333;
|
||||||
@@ -111,6 +121,10 @@ h3 {
|
|||||||
font-family: Arial;
|
font-family: Arial;
|
||||||
margin-bottom: 30px;
|
margin-bottom: 30px;
|
||||||
|
|
||||||
|
&.big {
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
@include desktop {
|
@include desktop {
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user