QuickUploader.vue 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. <template>
  2. <div id="quick-uploader">
  3. <!-- static landing UI -->
  4. <div v-show="!(showStream && canStream)" class="container has-text-centered">
  5. <div class="columns quick-uploader">
  6. <!-- trailer phrase that invite to add an account -->
  7. <div class="column is-full quick-uploader-header" :class="{ 'is-invisible' : !showTrailer }">
  8. {{ $t('twofaccounts.no_account_here') }}<br>
  9. {{ $t('twofaccounts.add_first_account') }}
  10. </div>
  11. <!-- add button -->
  12. <div class="column is-full quick-uploader-button" >
  13. <div class="quick-uploader-centerer">
  14. <!-- scan button that launch camera stream -->
  15. <label v-if="canStream" class="button is-link is-medium is-rounded is-focused" @click="enableStream()">
  16. {{ $t('twofaccounts.forms.scan_qrcode') }}
  17. </label>
  18. <!-- or classic input field -->
  19. <form v-else @submit.prevent="createAccount" @keydown="form.onKeydown($event)">
  20. <label :class="{'is-loading' : form.isBusy}" class="button is-link is-medium is-rounded is-focused">
  21. <input v-if="$root.appSettings.useBasicQrcodeReader" class="file-input" type="file" accept="image/*" v-on:change="uploadQrcode" ref="qrcodeInput">
  22. <qrcode-capture v-else @decode="uploadQrcode" class="file-input" ref="qrcodeInput" />
  23. {{ $t('twofaccounts.forms.use_qrcode.val') }}
  24. </label>
  25. <field-error :form="form" field="qrcode" />
  26. <field-error :form="form" field="uri" />
  27. </form>
  28. </div>
  29. </div>
  30. <!-- Fallback link to classic form -->
  31. <div class="column is-full quick-uploader-footer">
  32. <router-link :to="{ name: 'createAccount' }" class="is-link">{{ $t('twofaccounts.use_full_form') }}</router-link>
  33. </div>
  34. </div>
  35. </div>
  36. <!-- camera stream fullscreen scanner -->
  37. <div v-show="showStream && canStream">
  38. <div class="fullscreen-alert has-text-centered">
  39. <span class="is-size-4 has-text-light">
  40. <font-awesome-icon :icon="['fas', 'spinner']" size="2x" spin />
  41. </span>
  42. </div>
  43. <div class="fullscreen-streamer">
  44. <qrcode-stream @decode="uploadQrcode" @init="onStreamerInit" :camera="camera" />
  45. </div>
  46. <div class="fullscreen-footer">
  47. <!-- Cancel button -->
  48. <label class="button is-large is-warning is-rounded" @click="disableStream()">
  49. {{ $t('commons.cancel') }}
  50. </label>
  51. </div>
  52. </div>
  53. </div>
  54. </template>
  55. <script>
  56. import Form from './Form'
  57. export default {
  58. name: 'QuickUploader',
  59. data(){
  60. return {
  61. form: new Form({
  62. qrcode: null,
  63. uri: '',
  64. }),
  65. errorName: '',
  66. errorText: '',
  67. showStream: false,
  68. canStream: true,
  69. camera: 'auto',
  70. }
  71. },
  72. props: {
  73. showTrailer: {
  74. type: Boolean,
  75. default: false
  76. },
  77. directStreaming: {
  78. type: Boolean,
  79. default: true
  80. },
  81. },
  82. created() {
  83. if( this.$root.appSettings.useBasicQrcodeReader ) {
  84. // User has set the basic QR code reader so we disable the modern one
  85. this.canStream = this.showStream = false
  86. }
  87. else {
  88. if( this.directStreaming ) {
  89. this.enableStream()
  90. }
  91. }
  92. },
  93. beforeDestroy() {
  94. this.form.clear()
  95. },
  96. methods: {
  97. async enableStream() {
  98. this.$parent.$emit('initStreaming')
  99. this.camera = 'auto'
  100. this.showStream = true
  101. console.log('stream enabled')
  102. },
  103. async disableStream() {
  104. this.camera = 'off'
  105. this.showStream = false
  106. this.$parent.$emit('stopStreaming')
  107. console.log('stream disabled')
  108. },
  109. async onStreamerInit (promise) {
  110. this.errorText = ''
  111. this.errorName = ''
  112. try {
  113. await promise
  114. }
  115. catch (error) {
  116. this.errorName = error.name
  117. if (error.name === 'NotAllowedError') {
  118. this.errorText = this.$t('twofaccounts.stream.need_grant_permission')
  119. } else if (error.name === 'NotReadableError') {
  120. this.errorText = this.$t('twofaccounts.stream.not_readable')
  121. } else if (error.name === 'NotFoundError') {
  122. this.errorText = this.$t('twofaccounts.stream.no_cam_on_device')
  123. } else if (error.name === 'NotSupportedError' || error.name === 'InsecureContextError') {
  124. this.errorText = this.$t('twofaccounts.stream.secured_context_required')
  125. } else if (error.name === 'OverconstrainedError') {
  126. this.errorText = this.$t('twofaccounts.stream.camera_not_suitable')
  127. } else if (error.name === 'StreamApiNotSupportedError') {
  128. this.errorText = this.$t('twofaccounts.stream.stream_api_not_supported')
  129. }
  130. }
  131. this.setUploader()
  132. },
  133. setUploader() {
  134. if( this.errorName ) {
  135. this.canStream = false
  136. this.$parent.$emit('cannotStream')
  137. console.log('fail to stream : ' + this.errorText)
  138. if (this.errorName === 'NotAllowedError') {
  139. this.$notify({ type: 'is-danger', text: this.errorText })
  140. }
  141. if (this.errorName === 'InsecureContextError') {
  142. this.$notify({ type: 'is-warning', text: "HTTPS required for camera streaming" })
  143. }
  144. }
  145. if( !this.errorName && !this.showStream ) {
  146. this.camera = 'off'
  147. console.log('stream stopped')
  148. }
  149. if( this.canStream && this.showStream) {
  150. this.$parent.$emit('startStreaming')
  151. console.log('stream started')
  152. }
  153. },
  154. async uploadQrcode(event) {
  155. var response
  156. if(this.$root.appSettings.useBasicQrcodeReader) {
  157. let imgdata = new FormData();
  158. imgdata.append('qrcode', this.$refs.qrcodeInput.files[0]);
  159. response = await this.form.upload('/api/qrcode/decode', imgdata)
  160. }
  161. else {
  162. // We post the decoded URI instead of an image to decode
  163. this.form.uri = event
  164. if( !this.form.uri ) {
  165. return false
  166. }
  167. response = await this.form.post('/api/qrcode/decode')
  168. }
  169. this.$router.push({ name: 'createAccount', params: { qrAccount: response.data } });
  170. },
  171. }
  172. };
  173. </script>