From 0d84a75778464955e6638b937bbcedf56e2de26e Mon Sep 17 00:00:00 2001 From: Bubka <858858+Bubka@users.noreply.github.com> Date: Mon, 23 Nov 2020 19:30:58 +0100 Subject: [PATCH] Move live scanner to a dedicated view to make things easier --- resources/js/app.js | 1 - resources/js/packages/qrcodeReader.js | 4 - resources/js/routes.js | 2 + resources/js/views/Accounts.vue | 3 + resources/js/views/Capture.vue | 118 ++++++++++++ resources/js/views/Start.vue | 251 ++++++-------------------- resources/lang/en/twofaccounts.php | 31 +++- resources/sass/app.scss | 1 + 8 files changed, 200 insertions(+), 211 deletions(-) delete mode 100644 resources/js/packages/qrcodeReader.js create mode 100644 resources/js/views/Capture.vue diff --git a/resources/js/app.js b/resources/js/app.js index 9bcfb648..6836241a 100644 --- a/resources/js/app.js +++ b/resources/js/app.js @@ -5,7 +5,6 @@ import api from './api' import i18n from './langs/i18n' import FontAwesome from './packages/fontawesome' import Clipboard from './packages/clipboard' -import QrcodeReader from './packages/qrcodeReader' import Notifications from 'vue-notification' import './components' diff --git a/resources/js/packages/qrcodeReader.js b/resources/js/packages/qrcodeReader.js deleted file mode 100644 index 8b09b879..00000000 --- a/resources/js/packages/qrcodeReader.js +++ /dev/null @@ -1,4 +0,0 @@ -import Vue from 'vue' -import QrcodeReader from 'vue-qrcode-reader' - -Vue.use(QrcodeReader) \ No newline at end of file diff --git a/resources/js/routes.js b/resources/js/routes.js index 7a01f4b4..fadd1629 100644 --- a/resources/js/routes.js +++ b/resources/js/routes.js @@ -4,6 +4,7 @@ import Router from 'vue-router' Vue.use(Router) import Start from './views/Start' +import Capture from './views/Capture' import Accounts from './views/Accounts' import CreateAccount from './views/twofaccounts/Create' import EditAccount from './views/twofaccounts/Edit' @@ -22,6 +23,7 @@ const router = new Router({ mode: 'history', routes: [ { path: '/start', name: 'start', component: Start, meta: { requiresAuth: true }, props: true }, + { path: '/capture', name: 'capture', component: Capture, meta: { requiresAuth: true }, props: true }, { path: '/accounts', name: 'accounts', component: Accounts, meta: { requiresAuth: true }, alias: '/', props: true }, { path: '/account/create', name: 'createAccount', component: CreateAccount, meta: { requiresAuth: true } }, diff --git a/resources/js/views/Accounts.vue b/resources/js/views/Accounts.vue index 1d6d3337..2c2393b0 100644 --- a/resources/js/views/Accounts.vue +++ b/resources/js/views/Accounts.vue @@ -304,6 +304,9 @@ if( this.$root.appSettings.useDirectCapture && this.$root.appSettings.defaultCaptureMode === 'advancedForm' ) { this.$router.push({ name: 'createAccount' }) } + else if( this.$root.appSettings.useDirectCapture && this.$root.appSettings.defaultCaptureMode === 'livescan' ) { + this.$router.push({ name: 'capture' }) + } else { this.$router.push({ name: 'start' }) } diff --git a/resources/js/views/Capture.vue b/resources/js/views/Capture.vue new file mode 100644 index 00000000..61ae7072 --- /dev/null +++ b/resources/js/views/Capture.vue @@ -0,0 +1,118 @@ + + + \ No newline at end of file diff --git a/resources/js/views/Start.vue b/resources/js/views/Start.vue index 1b2cbed5..cb0e6547 100644 --- a/resources/js/views/Start.vue +++ b/resources/js/views/Start.vue @@ -1,76 +1,53 @@ @@ -82,27 +59,12 @@ * route: '/start' * * Offer the user all available possibilities for capturing an account : - * - By decoding a QR code acquired by live scan + * - By sending the user to the live scanner * - By decoding a QR code submitted with a form 'File' field - * - By browsing to the advanced form - * - * 2 QR code decoders are implemented : - * - vue-qrcode-reader (the default one) - * ~ QR codes are acquired by live scan or with a 'File' field by the js front-end - * ~ Decoding is done by the js front-end, there is no call to the back-end API - * - chillerlan/php-qrcode (aka 'BasicQrcodeReader') - * ~ QR codes are acquired with a 'File' field and uploaded to the php backend - * ~ Decoding is done by the php back-end - * - * Output : both decoders provide an URI and push it the Create form - * - * The view behavior is affected by the user options : - * - 'appSettings.useBasicQrcodeReader' totally disable the vue-qrcode-reader decoder - * - 'appSettings.useDirectCapture' trigger the acquisition mode set by 'appSettings.defaultCaptureMode' automatically at vue @created event + * - By sending the user to the advanced form * */ - import Form from './../components/Form' export default { name: 'Start', @@ -110,20 +72,9 @@ data(){ return { accountCount: null, - form: new Form({ - qrcode: null, - uri: '', - }), - errorName: '', - errorText: '', - showStream: false, - canStream: null, - camera: 'auto', } }, - // props: ['accountCount'], - mounted() { this.axios.get('api/twofaccounts/count').then(response => { @@ -138,94 +89,12 @@ this.$refs.qrcodeInputLabel.click() } }) - - if( this.$root.appSettings.useBasicQrcodeReader ) { - // User has set the basic QR code reader (run by backend) so we disable the other one (run by js) - this.canStream = this.showStream = false - } - else if( this.accountCount > 0 && this.$root.appSettings.useDirectCapture ) { - if( this.$root.appSettings.defaultCaptureMode === 'livescan' ) { - this.enableStream() - } - } - }, - - beforeDestroy() { - this.form.clear() }, methods: { - async enableStream() { - - this.camera = 'auto' - - if( this.canStream ) { - this.showStream = true - - console.log('stream started') - } - else if( this.errorText && !this.$root.appSettings.useBasicQrcodeReader ) { - this.$notify({ type: 'is-warning', text: this.errorText }) - } - }, - - async disableStream() { - - this.camera = 'off' - this.showStream = false - - console.log('stream stopped') - }, - - async onStreamerInit (promise) { - - this.errorText = '' - this.errorName = '' - - try { - await promise - - this.canStream = true - console.log('stream is possible') - } - catch (error) { - - this.errorName = error.name - - if (error.name === 'NotAllowedError') { - this.errorText = this.$t('twofaccounts.stream.need_grant_permission') - - } else if (error.name === 'NotReadableError') { - this.errorText = this.$t('twofaccounts.stream.not_readable') - - } else if (error.name === 'NotFoundError') { - this.errorText = this.$t('twofaccounts.stream.no_cam_on_device') - - } else if (error.name === 'NotSupportedError' || error.name === 'InsecureContextError') { - this.errorText = this.$t('twofaccounts.stream.secured_context_required') - - } else if (error.name === 'OverconstrainedError') { - this.errorText = this.$t('twofaccounts.stream.camera_not_suitable') - - } else if (error.name === 'StreamApiNotSupportedError') { - this.errorText = this.$t('twofaccounts.stream.stream_api_not_supported') - } - - this.canStream = false - - if( !this.$root.appSettings.useBasicQrcodeReader && this.$root.appSettings.useDirectCapture && this.$root.appSettings.defaultCaptureMode === 'livescan') { - this.$notify({ type: 'is-warning', text: this.errorText }) - } - - console.log('stream no possible : ' + this.errorText) - } - }, - - /** - * the basicQRcodeReader option is On, so qrcode decoding will be done by the backend, which in return - * send the corresponding URI + * Send the submitted QR code to the backend for decoding then push ser to the create form */ async submitQrCode() { @@ -235,34 +104,16 @@ const { data } = await this.form.upload('/api/qrcode/decode', imgdata) - this.pushUriToCreateForm(data.uri) + this.$router.push({ name: 'createAccount', params: { decodedUri: data.uri } }); }, - /** - * The basicQRcodeReader option is Off, so qrcode decoding has already be done by vue-qrcode-reader, whether - * from livescan or file input. - * We simply check the uri validity to prevent useless push to the Create form, but the form will check uri validity too. + * Push user to the dedicated capture view for live scan */ - async submitUri(event) { - - this.form.uri = event - - if( !this.form.uri ) { - this.$notify({type: 'is-warning', text: this.$t('errors.qrcode_cannot_be_read') }) - } - else if( this.form.uri.slice(0, 15 ).toLowerCase() !== "otpauth://totp/" && this.form.uri.slice(0, 15 ).toLowerCase() !== "otpauth://hotp/" ) { - this.$notify({type: 'is-warning', text: this.$t('errors.no_valid_otp') }) - } - else { - this.pushUriToCreateForm(this.form.uri) - } + capture() { + this.$router.push({ name: 'capture' }); }, - pushUriToCreateForm(data) { - this.$router.push({ name: 'createAccount', params: { decodedUri: data } }); - } - } }; diff --git a/resources/lang/en/twofaccounts.php b/resources/lang/en/twofaccounts.php index 0e894735..199f8b96 100644 --- a/resources/lang/en/twofaccounts.php +++ b/resources/lang/en/twofaccounts.php @@ -87,13 +87,32 @@ 'alternative_methods' => 'Alternative methods', ], 'stream' => [ - 'need_grant_permission' => 'You need to grant camera access permission', - 'not_readable' => 'Fail to load scanner. Is the camera already in use?', - 'no_cam_on_device' => 'No camera on this device', - 'secured_context_required' => 'Secure context required (HTTPS or localhost)', + 'live_scan_cant_start' => 'Live scan can\'t start :(', + 'need_grant_permission' => [ + 'reason' => '2FAuth does not have permission to access your camera', + 'solution' => 'You need to grant permission to use your device camera. If you already denied and your browser do not prompt you again, please refers to the browser documentation to find out how to grant permission.' + ], + 'not_readable' => [ + 'reason' => 'Fail to load scanner', + 'solution' => 'Is the camera already in use? Ensure that no other app use your camera and try again' + ], + 'no_cam_on_device' => [ + 'reason' => 'No camera on this device', + 'solution' => 'Maybe your forget to plug in your webcam' + ], + 'secured_context_required' => [ + 'reason' => 'Secure context required', + 'solution' => 'HTTPS is required for live scan. If you run 2FAuth from your computer, do not use virtual host other than localhost' + ], 'https_required' => 'HTTPS required for camera streaming', - 'camera_not_suitable' => 'Installed cameras are not suitable', - 'stream_api_not_supported' => 'Stream API is not supported in this browser' + 'camera_not_suitable' => [ + 'reason' => 'Installed cameras are not suitable', + 'solution' => 'Please use another device/camera' + ], + 'stream_api_not_supported' => [ + 'reason' => 'Stream API is not supported in this browser', + 'solution' => 'You should use a modern browser' + ], ], 'confirm' => [ 'delete' => 'Are you sure you want to delete this account?', diff --git a/resources/sass/app.scss b/resources/sass/app.scss index afa7f4af..d5c27a62 100644 --- a/resources/sass/app.scss +++ b/resources/sass/app.scss @@ -227,6 +227,7 @@ a:hover { left: 0; width: 100%; height: 65%; + padding: 2%; } /* Corner borders */