free-floating/src/views/Unlock.vue

197 lines
4.0 KiB
Vue

<template>
<div class="container">
<h2 class="inquadra">
Inquadra il qrcode
</h2>
<div class="camera">
<qrcode-drop-zone @decode="onDecode" @init="logErrors">
<qrcode-stream :camera="camera" @decode="onDecode" @init="onInit" />
</qrcode-drop-zone>
<qrcode-capture v-if="noStreamApiSupport" @decode="onDecode" />
</div>
<p v-if="isInvalid">non è un qrcode di ofo 😟</p>
<p v-if="errored">si è verificato un errore, riprova!</p>
<div v-if="loading" class="loading">⏳⏳⏳</div>
<div class="decode-result">
<div
class="decode-number"
v-for="letter in split(bikePin)"
v-bind:key="letter"
>
{{ letter }}
</div>
</div>
<p v-if="bikePin">
🔓inserisci il codice! ricordati che il codice è unico per ogni
bicicletta, riusalo 🤯
</p>
</div>
</template>
<script>
import { QrcodeStream, QrcodeDropZone, QrcodeCapture } from "vue-qrcode-reader";
import axios from "axios";
export default {
name: "Unlock",
components: { QrcodeStream, QrcodeDropZone, QrcodeCapture },
data() {
return {
result: "",
error: "",
loading: false,
bikePin: "",
errored: false,
noStreamApiSupport: false,
camera: "auto",
isInvalid: undefined
};
},
methods: {
async onDecode(result) {
this.bikePin = "";
this.errored = false;
this.turnCameraOff();
if (result.startsWith("http://ofo.com/oneplate/")) {
const code = +result.split("/").slice(-1);
this.result = code;
this.getCode(code);
} else {
this.isInvalid = true;
}
await this.timeout(3000);
this.turnCameraOn();
this.isInvalid = undefined;
},
logErrors(promise) {
promise.catch(console.error);
},
async onInit(promise) {
try {
await promise;
} catch (error) {
if (error.name === "StreamApiNotSupportedError") {
this.noStreamApiSupport = true;
}
}
},
resetValidationState() {
this.isInvalid = undefined;
},
getCode(bikeId) {
this.loading = true;
axios
.get("https://api.coindesk.com/v1/bpi/currentprice.json") //add here the api enpoint w/ bikeId param
.then(response => {
//const pin = response.data //get pin from api response
const pin = "1234";
this.bikePin = pin;
})
.catch(error => {
this.errored = true;
this.result = "";
})
.finally(() => {
this.result = "";
this.loading = false;
});
},
turnCameraOn() {
this.camera = "auto";
},
turnCameraOff() {
this.camera = "off";
},
timeout(ms) {
return new Promise(resolve => {
window.setTimeout(resolve, ms);
});
},
split(text) {
return text.split("");
}
}
};
</script>
<style scoped>
.container {
max-width: 500px;
height: 100%;
padding: 15px;
margin: 0 auto;
}
.camera {
display: flex;
align-items: center;
justify-content: center;
}
.camera .wrapper::before {
top: -6px;
bottom: -6px;
left: 45px;
right: 45px;
content: "";
display: block;
position: absolute;
background: var(--yellow-ofo);
z-index: 0;
}
.camera .wrapper::after {
left: -6px;
right: -6px;
top: 45px;
bottom: 45px;
content: "";
display: block;
position: absolute;
background: var(--yellow-ofo);
z-index: 0;
}
.camera .wrapper {
padding: 10px;
border: 5px solid black;
margin: 0 auto;
border-radius: 5px;
position: relative;
}
.error {
font-weight: bold;
color: red;
}
.inquadra,
.loading {
text-align: center;
}
.inquadra {
font-family: "syne", serif;
font-weight: 700;
text-transform: uppercase;
}
.decode-result {
display: flex;
align-items: center;
justify-content: center;
margin-top: 25px;
}
.decode-number {
padding: 15px;
border: 2px solid black;
margin: 5px;
font-weight: bold;
font-size: 1.5rem;
}
</style>