Show content of non-OTP QR code after a live scan for copying or browsing

This commit is contained in:
Bubka 2023-11-28 14:08:25 +01:00
parent 685d2bae92
commit 96391fd1d5
4 changed files with 78 additions and 56 deletions

View File

@ -0,0 +1,69 @@
<script setup>
const { copy } = useClipboard({ legacy: true })
import { useNotifyStore } from '@/stores/notify'
import { UseColorMode } from '@vueuse/components'
const notify = useNotifyStore()
const props = defineProps({
qrContent: String,
})
/**
* Checks if a string is an url
*
* @param {string} str
*/
function isUrl(str) {
var strRegex = /^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]+$/
var re = new RegExp(strRegex)
return re.test(str)
}
/**
* Opens an uri in a new browser window
*
* @param {string} uri
*/
function openInBrowser(uri) {
const a = document.createElement('a')
a.setAttribute('href', uri)
a.dispatchEvent(new MouseEvent("click", { 'view': window, 'bubbles': true, 'cancelable': true }))
}
/**
* Copies to clipboard and notify
*
* @param {*} data
*/
function copyToClipboard(data) {
copy(data)
notify.success({ text: trans('commons.copied_to_clipboard') })
}
</script>
<template>
<div class="too-bad"></div>
<div class="block">
{{ $t('errors.data_of_qrcode_is_not_valid_URI') }}
</div>
<UseColorMode v-slot="{ mode }">
<div class="block mb-6" :class="mode == 'dark' ? 'has-text-light':'has-text-grey-dark'">{{ qrContent ? qrContent : '[' + trans('commons.nothing') + ']' }}</div>
</UseColorMode>
<!-- Copy to clipboard -->
<div class="block has-text-link" v-if="qrContent">
<button class="button is-link is-outlined is-rounded" @click.stop="copyToClipboard(qrContent)">
{{ $t('commons.copy_to_clipboard') }}
</button>
</div>
<!-- Open in browser -->
<div class="block has-text-link" v-if="isUrl(qrContent)" @click="openInBrowser(qrContent)">
<button class="button is-link is-outlined is-rounded">
<span>{{ $t('commons.open_in_browser') }}</span>
<span class="icon is-small">
<FontAwesomeIcon :icon="['fas', 'external-link-alt']" />
</span>
</button>
</div>
</template>

View File

@ -1,6 +1,7 @@
<script setup>
import Form from '@/components/formElements/Form'
import Spinner from '@/components/Spinner.vue'
import QrContentDisplay from '@/components/QrContentDisplay.vue'
import { useBusStore } from '@/stores/bus'
import { UseColorMode } from '@vueuse/components'
import { useNotifyStore } from '@/stores/notify'
@ -18,6 +19,7 @@
qrcode: null,
uri: '',
}))
const showQrContent = ref(false)
onMounted(async () => {
devices.value = (await navigator.mediaDevices.enumerateDevices())
@ -65,6 +67,7 @@
router.push({ name: 'importAccounts' })
}
else if (form.uri.slice(0, 15).toLowerCase() !== "otpauth://totp/" && form.uri.slice(0, 15).toLowerCase() !== "otpauth://hotp/") {
showQrContent.value = true
notify.warn({ text: trans('errors.no_valid_otp') })
}
else {
@ -200,4 +203,7 @@
<ButtonBackCloseCancel action="cancel" :isCapture="true" :useLinkTag="false" @canceled="exitStream()" />
</div>
</div>
<modal v-model="showQrContent">
<QrContentDisplay :qrContent="form.uri" />
</modal>
</template>

View File

@ -1,6 +1,7 @@
<script setup>
import Form from '@/components/formElements/Form'
import OtpDisplay from '@/components/OtpDisplay.vue'
import QrContentDisplay from '@/components/QrContentDisplay.vue'
import FormLockField from '@/components/formelements/FormLockField.vue'
import twofaccountService from '@/services/twofaccountService'
import { useUserStore } from '@/stores/user'
@ -9,7 +10,6 @@
import { useNotifyStore } from '@/stores/notify'
import { UseColorMode } from '@vueuse/components'
const { copy } = useClipboard({ legacy: true })
const $2fauth = inject('2fauth')
const router = useRouter()
const route = useRoute()
@ -347,39 +347,6 @@
return str.replace(/(<([^> ]+)>)/ig, "")
}
/**
* Checks if a string is an url
*
* @param {string} str
*/
function isUrl(str) {
var strRegex = /^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]+$/
var re = new RegExp(strRegex)
return re.test(str)
}
/**
* Opens an uri in a new browser window
*
* @param {string} uri
*/
function openInBrowser(uri) {
const a = document.createElement('a')
a.setAttribute('href', uri)
a.dispatchEvent(new MouseEvent("click", { 'view': window, 'bubbles': true, 'cancelable': true }))
}
/**
* Copies to clipboard and notify
*
* @param {*} data
*/
function copyToClipboard(data) {
copy(data)
notify.success({ text: trans('commons.copied_to_clipboard') })
}
</script>
<template>
@ -534,28 +501,7 @@
</FormWrapper>
<!-- alternatives -->
<modal v-model="showAlternatives">
<div class="too-bad"></div>
<div class="block">
{{ $t('errors.data_of_qrcode_is_not_valid_URI') }}
</div>
<UseColorMode v-slot="{ mode }">
<div class="block mb-6" :class="mode == 'dark' ? 'has-text-light':'has-text-grey-dark'">{{ uri }}</div>
</UseColorMode>
<!-- Copy to clipboard -->
<div class="block has-text-link">
<button class="button is-link is-outlined is-rounded" @click.stop="copyToClipboard(uri)">
{{ $t('commons.copy_to_clipboard') }}
</button>
</div>
<!-- Open in browser -->
<div class="block has-text-link" v-if="isUrl(uri)" @click="openInBrowser(uri)">
<button class="button is-link is-outlined is-rounded">
<span>{{ $t('commons.open_in_browser') }}</span>
<span class="icon is-small">
<FontAwesomeIcon :icon="['fas', 'external-link-alt']" />
</span>
</button>
</div>
<QrContentDisplay :qrContent="uri" />
</modal>
</div>
</template>

View File

@ -75,4 +75,5 @@
'submit' => 'Submit',
'default' => 'Default',
'back_to_home' => 'Back to home',
'nothing' => 'nothing',
];