Add QR scanner to Unseal page. Closes #22.
This commit is contained in:
parent
228901a68c
commit
18f034acd3
|
@ -13,6 +13,8 @@
|
||||||
"mini-css-extract-plugin": "^1.4.1",
|
"mini-css-extract-plugin": "^1.4.1",
|
||||||
"node-sass": "^5.0.0",
|
"node-sass": "^5.0.0",
|
||||||
"prismjs": "^1.23.0",
|
"prismjs": "^1.23.0",
|
||||||
|
"qr-scanner": "^1.2.0",
|
||||||
|
"raw-loader": "^4.0.2",
|
||||||
"sass-loader": "^11.0.1",
|
"sass-loader": "^11.0.1",
|
||||||
"uikit": "^3.6.19",
|
"uikit": "^3.6.19",
|
||||||
"webpack": "^5.32.0",
|
"webpack": "^5.32.0",
|
||||||
|
|
|
@ -2,14 +2,103 @@ import { Page } from "../types/Page.js";
|
||||||
import { submitUnsealKey, getSealStatus } from "../api.js";
|
import { submitUnsealKey, getSealStatus } from "../api.js";
|
||||||
import { setPageContent, setErrorText, changePage } from "../pageUtils.js";
|
import { setPageContent, setErrorText, changePage } from "../pageUtils.js";
|
||||||
import { makeElement } from "../htmlUtils.js";
|
import { makeElement } from "../htmlUtils.js";
|
||||||
|
import { Margin } from "../elements/Margin.js";
|
||||||
import { MarginInline } from "../elements/MarginInline.js";
|
import { MarginInline } from "../elements/MarginInline.js";
|
||||||
import i18next from 'i18next';
|
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 {
|
export class UnsealPage extends Page {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
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() {
|
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({
|
this.unsealKeyForm = makeElement({
|
||||||
tag: "form",
|
tag: "form",
|
||||||
children: [
|
children: [
|
||||||
|
@ -30,36 +119,44 @@ export class UnsealPage extends Page {
|
||||||
})),
|
})),
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
this.unsealInputContent.appendChild(this.unsealKeyForm);
|
||||||
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.unsealKeyForm.addEventListener("submit", function (e) {
|
this.unsealKeyForm.addEventListener("submit", function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
pageState.currentPage.handleKeySubmit();
|
this.handleKeySubmit();
|
||||||
});
|
}.bind(this));
|
||||||
this.updateSealProgress(await getSealStatus());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
updateSealProgress(data) {
|
||||||
let progress = data.progress;
|
let progress = data.progress;
|
||||||
let keysNeeded = data.t;
|
let keysNeeded = data.t;
|
||||||
|
@ -77,10 +174,8 @@ export class UnsealPage extends Page {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async handleKeySubmit() {
|
submitKey(key) {
|
||||||
let formData = new FormData(this.unsealKeyForm);
|
submitUnsealKey(key).then(_ => {
|
||||||
|
|
||||||
submitUnsealKey(formData.get("key")).then(_ => {
|
|
||||||
getSealStatus().then(data => {
|
getSealStatus().then(data => {
|
||||||
this.updateSealProgress(data);
|
this.updateSealProgress(data);
|
||||||
});
|
});
|
||||||
|
@ -88,6 +183,12 @@ export class UnsealPage extends Page {
|
||||||
setErrorText(e.message);
|
setErrorText(e.message);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async handleKeySubmit() {
|
||||||
|
let formData = new FormData(this.unsealKeyForm);
|
||||||
|
|
||||||
|
this.submitKey(formData.get("key"))
|
||||||
|
}
|
||||||
get name() {
|
get name() {
|
||||||
return i18next.t("unseal_vault_text");
|
return i18next.t("unseal_vault_text");
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ $global-warning-background: #d08770;
|
||||||
$global-danger-background: #bf616a;
|
$global-danger-background: #bf616a;
|
||||||
|
|
||||||
$button-primary-background: #5e81ac;
|
$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
|
// Keep these in same order as https://github.com/uikit/uikit/blob/develop/src/less/components/_import.less
|
||||||
@import "uikit/src/scss/variables.scss";
|
@import "uikit/src/scss/variables.scss";
|
||||||
|
|
|
@ -26,6 +26,9 @@ module.exports = {
|
||||||
// Unseal Page
|
// Unseal Page
|
||||||
"unseal_vault_text": "Unseal the Vault",
|
"unseal_vault_text": "Unseal the Vault",
|
||||||
"submit_key_btn": "Submit Key",
|
"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",
|
"key_input_placeholder": "Key",
|
||||||
"unseal_keys_progress": "Keys: {{progress}}/{{keys_needed}}",
|
"unseal_keys_progress": "Keys: {{progress}}/{{keys_needed}}",
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue