diff --git a/package-lock.json b/package-lock.json index d2c2a25..e6656ea 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,7 @@ "base32-encode": "^2.0.0", "bulma": "^0.9.4", "vue": "^3.3.4", + "vue-qrcode-reader": "^5.1.0", "vue-router": "^4.2.4" }, "devDependencies": { @@ -401,6 +402,28 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/@sec-ant/barcode-detector": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@sec-ant/barcode-detector/-/barcode-detector-0.1.5.tgz", + "integrity": "sha512-l6ULVNfp4T1U2JjFTwW26ZZoWRTxCNsXfE1Bk74/1JB1eK6hHPBxVugs3pBUqV75A0inou5S9S+ay4sRBrjieQ==", + "dependencies": { + "@sec-ant/zxing-wasm": "^1.2.4" + } + }, + "node_modules/@sec-ant/zxing-wasm": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@sec-ant/zxing-wasm/-/zxing-wasm-1.2.4.tgz", + "integrity": "sha512-8avz7BHc8aa+k0Jym/1dJEOlqsYEZkoqiFtSVXbmxMwWXG7+OJCJBEWRR8VJPOlVmQj4pSVWtJr/dMuIf1TM/A==", + "dependencies": { + "@types/emscripten": "^1.39.6", + "zustand": "^4.3.8" + } + }, + "node_modules/@types/emscripten": { + "version": "1.39.7", + "resolved": "https://registry.npmjs.org/@types/emscripten/-/emscripten-1.39.7.tgz", + "integrity": "sha512-tLqYV94vuqDrXh515F/FOGtBcRMTPGvVV1LzLbtYDcQmmhtpf/gLYf+hikBbQk8MzOHNz37wpFfJbYAuSn8HqA==" + }, "node_modules/@types/web-bluetooth": { "version": "0.0.17", "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.17.tgz", @@ -819,6 +842,24 @@ "node": ">=0.12.0" } }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "peer": true + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "peer": true, + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, "node_modules/magic-string": { "version": "0.30.1", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.1.tgz", @@ -900,6 +941,18 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/react": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", + "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "peer": true, + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -945,6 +998,11 @@ "node": ">=14.0.0" } }, + "node_modules/sdp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/sdp/-/sdp-3.2.0.tgz", + "integrity": "sha512-d7wDPgDV3DDiqulJjKiV2865wKsJ34YI+NDREbm+FySq6WuKOikwyNQcm+doLAZ1O6ltdO0SeKle2xMpN3Brgw==" + }, "node_modules/source-map-js": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", @@ -973,6 +1031,14 @@ "node": ">=8.0" } }, + "node_modules/use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/vite": { "version": "4.4.7", "resolved": "https://registry.npmjs.org/vite/-/vite-4.4.7.tgz", @@ -1040,6 +1106,18 @@ "@vue/shared": "3.3.4" } }, + "node_modules/vue-qrcode-reader": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/vue-qrcode-reader/-/vue-qrcode-reader-5.1.0.tgz", + "integrity": "sha512-jOlvpTSMQ0ttYl582VwvCOcO4qNJId0lRN3Cl+Czgwoml6pOVAnwBQb5VMPxxJiwI/nIuKzXxcPoAixf/VMrJw==", + "dependencies": { + "@sec-ant/barcode-detector": "^0.1.5", + "webrtc-adapter": "^8.2.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/vue-router": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.2.4.tgz", @@ -1053,6 +1131,41 @@ "peerDependencies": { "vue": "^3.2.0" } + }, + "node_modules/webrtc-adapter": { + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/webrtc-adapter/-/webrtc-adapter-8.2.3.tgz", + "integrity": "sha512-gnmRz++suzmvxtp3ehQts6s2JtAGPuDPjA1F3a9ckNpG1kYdYuHWYpazoAnL9FS5/B21tKlhkorbdCXat0+4xQ==", + "dependencies": { + "sdp": "^3.2.0" + }, + "engines": { + "node": ">=6.0.0", + "npm": ">=3.10.0" + } + }, + "node_modules/zustand": { + "version": "4.3.9", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.3.9.tgz", + "integrity": "sha512-Tat5r8jOMG1Vcsj8uldMyqYKC5IZvQif8zetmLHs9WoZlntTHmIoNM8TpLRY31ExncuUvUOXehd0kvahkuHjDw==", + "dependencies": { + "use-sync-external-store": "1.2.0" + }, + "engines": { + "node": ">=12.7.0" + }, + "peerDependencies": { + "immer": ">=9.0", + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "immer": { + "optional": true + }, + "react": { + "optional": true + } + } } } } diff --git a/package.json b/package.json index 769c8b2..98d0e71 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "base32-encode": "^2.0.0", "bulma": "^0.9.4", "vue": "^3.3.4", + "vue-qrcode-reader": "^5.1.0", "vue-router": "^4.2.4" }, "devDependencies": { diff --git a/src/views/AddAccountView.vue b/src/views/AddAccountView.vue index d4eee17..1ad7968 100644 --- a/src/views/AddAccountView.vue +++ b/src/views/AddAccountView.vue @@ -2,6 +2,7 @@ import { ref, computed } from 'vue'; import { useRouter } from 'vue-router'; import { useStorage } from '@vueuse/core' +import { QrcodeStream } from 'vue-qrcode-reader' import { sha256 } from '@noble/hashes/sha256'; import base32Encode from 'base32-encode'; @@ -60,6 +61,18 @@ const addAccount = () => { } }; +// QR code reader +const scanQrCode = ref(false); +const showQrCodeScanner = (data) => { + scanQrCode.value = true; +}; +const onQrCodeDetected = (result_list) => { + if (result_list.length >= 1) { + privateKey.value = result_list[0].rawValue; + scanQrCode.value = false; + } +}; + // Cancel button const cancellDisabled = computed(() => { return !accounts.value.length; @@ -99,12 +112,16 @@ const resetErrorMessage = () => { -
- -
+ +
+
+

+ Scan +

+