145 lines
2.8 KiB
Vue
145 lines
2.8 KiB
Vue
<template>
|
|
<div
|
|
class="wrapper"
|
|
@mouseenter="[(otpShown = true), (iconOpacity = 1)]"
|
|
@mouseleave="
|
|
[(otpShown = showOtp === 'yes' ? true : tapped ? true : false), (iconOpacity = 0.1)]
|
|
"
|
|
>
|
|
<div class="card-top">
|
|
<span class="issuer">{{ issuer }}</span>
|
|
<button class="card-button" @click="editSecret">
|
|
<img src="../assets/edit-icon.png" class="button-icon" /></button
|
|
><br />
|
|
</div>
|
|
|
|
<span class="user">{{ username }}</span> <br />
|
|
<div class="otp-container">
|
|
<span class="otp" @click="[tapReveal($event), copyOtp($event)]">
|
|
<span v-if="otpShown">{{ this.otp }}</span>
|
|
<span v-else> •••••• </span>
|
|
</span>
|
|
<button class="card-button" @click="copyOtp">
|
|
<img src="../assets/copy-icon.png" class="button-icon" />
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import { TOTP } from "totp-generator";
|
|
export default {
|
|
props: {
|
|
id: Number,
|
|
issuer: String,
|
|
username: String,
|
|
secret: String,
|
|
showOtp: {
|
|
type: String,
|
|
default: "no",
|
|
},
|
|
},
|
|
data() {
|
|
return {
|
|
otp: 123456,
|
|
notes: "",
|
|
otpShown: false,
|
|
iconOpacity: 0.1,
|
|
tapped: false,
|
|
revealTime: 10,
|
|
};
|
|
},
|
|
|
|
methods: {
|
|
generateTotp() {
|
|
const { otp, expires } = TOTP.generate(this.secret);
|
|
const now = new Date();
|
|
const remainingTime = expires - now;
|
|
this.otp = otp;
|
|
setTimeout(this.generateTotp, remainingTime);
|
|
},
|
|
|
|
async copyOtp() {
|
|
await navigator.clipboard
|
|
.writeText(this.otp)
|
|
.then(() => {
|
|
console.log("copying successful");
|
|
})
|
|
.catch((err) => {
|
|
console.log("copy failed", err);
|
|
});
|
|
},
|
|
|
|
async tapReveal() {
|
|
console.log("tapped");
|
|
this.otpShown = true;
|
|
this.tapped = true;
|
|
setTimeout(() => {
|
|
this.otpShown = this.showOtp === "yes" ? true : false;
|
|
this.tapped = false;
|
|
}, this.revealTime * 1000);
|
|
},
|
|
|
|
editSecret() {
|
|
this.$emit("edit", this.id);
|
|
},
|
|
},
|
|
|
|
mounted() {
|
|
this.otpShown = this.showOtp === "yes" ? true : false;
|
|
this.generateTotp();
|
|
},
|
|
|
|
created() {},
|
|
};
|
|
</script>
|
|
|
|
<style>
|
|
.wrapper {
|
|
height: 90px;
|
|
width: 160px;
|
|
/* border: 1px solid black; */
|
|
border-radius: 4px;
|
|
padding: 4px 12px 4px 12px;
|
|
box-shadow: 1px 1px 8px #ccc;
|
|
margin: 2px;
|
|
}
|
|
|
|
.issuer {
|
|
font-weight: 700;
|
|
}
|
|
|
|
.user {
|
|
color: gray;
|
|
font-size: 0.8rem;
|
|
}
|
|
|
|
.otp {
|
|
margin-top: 0.4rem;
|
|
font-size: 1.8rem;
|
|
}
|
|
|
|
.card-top {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
}
|
|
.card-button {
|
|
position: relative;
|
|
right: 0;
|
|
margin-left: auto;
|
|
border: none;
|
|
background: none;
|
|
}
|
|
|
|
.button-icon {
|
|
width: 16px;
|
|
height: 16px;
|
|
opacity: v-bind("iconOpacity");
|
|
}
|
|
|
|
.otp-container {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
}
|
|
</style>
|