1
0
Fork 0

Add QR scanner to Unseal page. Closes #22.

This commit is contained in:
Kitteh 2021-04-24 11:19:21 +01:00
parent 228901a68c
commit 18f034acd3
4 changed files with 138 additions and 31 deletions

View file

@ -13,6 +13,8 @@
"mini-css-extract-plugin": "^1.4.1",
"node-sass": "^5.0.0",
"prismjs": "^1.23.0",
"qr-scanner": "^1.2.0",
"raw-loader": "^4.0.2",
"sass-loader": "^11.0.1",
"uikit": "^3.6.19",
"webpack": "^5.32.0",

View file

@ -2,14 +2,103 @@ import { Page } from "../types/Page.js";
import { submitUnsealKey, getSealStatus } from "../api.js";
import { setPageContent, setErrorText, changePage } from "../pageUtils.js";
import { makeElement } from "../htmlUtils.js";
import { Margin } from "../elements/Margin.js";
import { MarginInline } from "../elements/MarginInline.js";
import i18next from 'i18next';
import QrScanner from 'qr-scanner';
import qrScannerWorkerSource from '!!raw-loader!qr-scanner/qr-scanner-worker.min.js';
QrScanner.WORKER_PATH = URL.createObjectURL(new Blob([qrScannerWorkerSource]));
const UnsealInputModes = {
FORM_INPUT: "FORM_INPUT",
QR_INPUT: "QR_INPUT"
}
export class UnsealPage extends Page {
constructor() {
super();
//this.mode = UnsealInputModes.QR_INPUT;
this.mode = UnsealInputModes.FORM_INPUT;
}
cleanup() {
this.deinitWebcam()
clearInterval(this.refresher);
}
deinitWebcam() {
try {
this.stream.getTracks().forEach(function (track) {
track.stop();
});
} catch {
}
}
makeRefresher() {
this.refresher = setInterval(async function () {
this.updateSealProgress(await getSealStatus());
}.bind(this), 1000);
}
async render() {
this.unsealProgress = makeElement({
tag: "progress",
class: "uk-progress",
attributes: { value: "0", max: "0" }
});
this.unsealProgressText = makeElement({
tag: "p",
text: i18next.t("unseal_keys_progress", { progress: "0", keys_needed: "0" }),
});
this.unsealInputContent = makeElement({
tag: "div"
})
setPageContent(makeElement({
tag: "div",
children: [
this.unsealProgress,
makeElement({
tag: "p",
id: "errorText",
class: ["uk-text-danger", "uk-margin-top"]
}),
this.unsealProgressText,
this.unsealInputContent
]
}));
this.switchInputMode(this.mode);
this.updateSealProgress(await getSealStatus());
this.makeRefresher();
}
setButtons(method) {
let newMethod;
let buttonText;
newMethod = method == UnsealInputModes.FORM_INPUT ? UnsealInputModes.QR_INPUT : UnsealInputModes.FORM_INPUT;
buttonText = newMethod == UnsealInputModes.FORM_INPUT ? i18next.t("unseal_input_btn") : i18next.t("unseal_qr_btn");
this.unsealInputContent.appendChild(makeElement({
tag: "button",
class: ["uk-button", "uk-button-primary"],
text: buttonText,
onclick: () => {
this.switchInputMode(newMethod);
}
}))
}
switchInputMode(method) {
this.deinitWebcam();
this.unsealInputContent.querySelectorAll('*').forEach(n => n.remove())
if (method == UnsealInputModes.FORM_INPUT) this.makeUnsealForm();
if (method == UnsealInputModes.QR_INPUT) this.makeQRInput();
this.setButtons(method);
}
makeUnsealForm() {
this.unsealKeyForm = makeElement({
tag: "form",
children: [
@ -30,36 +119,44 @@ export class UnsealPage extends Page {
})),
]
});
this.unsealProgress = makeElement({
tag: "progress",
class: "uk-progress",
attributes: { value: "0", max: "0" }
});
this.unsealProgressText = makeElement({
tag: "p",
text: i18next.t("unseal_keys_progress", { progress: "0", keys_needed: "0" }),
});
setPageContent(makeElement({
tag: "div",
children: [
this.unsealProgress,
makeElement({
tag: "p",
id: "errorText",
class: ["uk-text-danger", "uk-margin-top"]
}),
this.unsealProgressText,
this.unsealKeyForm
]
}));
this.unsealInputContent.appendChild(this.unsealKeyForm);
this.unsealKeyForm.addEventListener("submit", function (e) {
e.preventDefault();
pageState.currentPage.handleKeySubmit();
});
this.updateSealProgress(await getSealStatus());
this.handleKeySubmit();
}.bind(this));
}
async makeQRInput() {
let webcamVideo = makeElement({
tag: "video"
})
let QRInput = makeElement({
tag: "div",
children: [
Margin(webcamVideo),
]
})
this.unsealInputContent.appendChild(QRInput);
this.stream = await navigator.mediaDevices.getUserMedia({
video: {
facingMode: 'environment',
},
audio: false,
});
webcamVideo.srcObject = this.stream;
let lastSeenCode = "";
const qrScanner = new QrScanner(webcamVideo, function (code) {
if (lastSeenCode == code) return;
lastSeenCode = code;
this.submitKey(code);
console.log('decoded qr code:', code)
}.bind(this));
qrScanner.start();
}
updateSealProgress(data) {
let progress = data.progress;
let keysNeeded = data.t;
@ -77,10 +174,8 @@ export class UnsealPage extends Page {
}
}
async handleKeySubmit() {
let formData = new FormData(this.unsealKeyForm);
submitUnsealKey(formData.get("key")).then(_ => {
submitKey(key) {
submitUnsealKey(key).then(_ => {
getSealStatus().then(data => {
this.updateSealProgress(data);
});
@ -88,6 +183,12 @@ export class UnsealPage extends Page {
setErrorText(e.message);
});
}
async handleKeySubmit() {
let formData = new FormData(this.unsealKeyForm);
this.submitKey(formData.get("key"))
}
get name() {
return i18next.t("unseal_vault_text");
}

View file

@ -18,6 +18,7 @@ $global-warning-background: #d08770;
$global-danger-background: #bf616a;
$button-primary-background: #5e81ac;
$progress-bar-background: #5e81ac;
// Keep these in same order as https://github.com/uikit/uikit/blob/develop/src/less/components/_import.less
@import "uikit/src/scss/variables.scss";

View file

@ -26,6 +26,9 @@ module.exports = {
// Unseal Page
"unseal_vault_text": "Unseal the Vault",
"submit_key_btn": "Submit Key",
"unseal_input_btn": "Switch to Manual Key Input",
"unseal_qr_btn": "Switch to QR Key Input",
"key_input_placeholder": "Key",
"unseal_keys_progress": "Keys: {{progress}}/{{keys_needed}}",