Pre-calculate next OTP & Add user pref to show/hide it - Closes #416

This commit is contained in:
Bubka
2025-03-03 17:02:27 +01:00
parent 47a13b891d
commit 46c1131b10
12 changed files with 140 additions and 39 deletions

View File

@ -45,10 +45,12 @@
image : ''
})
const password = ref('')
const next_password = ref('')
const generated_at = ref(null)
const hasTOTP = ref(false)
const showInlineSpinner = ref(false)
const showMainSpinner = ref(false)
const revealPassword = ref(false)
const opacity = ref('0')
const dots = ref()
const totpLooper = ref()
@ -136,11 +138,22 @@
* Requests and handles a fresh OTP
*/
async function getOtp() {
setLoadingState()
// We replace the current on screen password with the next_password to avoid having a loader.
// The next_password will be confirmed with a new request to be synced with the backend no matter what.
if (next_password.value) {
password.value = next_password.value
next_password.value = ''
dots.value.turnOff()
turnDotOn(0)
}
else {
setLoadingState()
}
await getOtpPromise().then(response => {
let otp = response.data
password.value = otp.password
next_password.value = otp.next_password
if(user.preferences.copyOtpOnDisplay) {
copyOTP(otp.password)
@ -169,7 +182,7 @@
//throw error
})
.finally(() => {
showInlineSpinner.value = false
showMainSpinner.value = false
})
}
@ -177,7 +190,7 @@
* Shows blacked dots and a loading spinner
*/
function setLoadingState() {
showInlineSpinner.value = true
showMainSpinner.value = true
dots.value.turnOff()
}
@ -212,6 +225,7 @@
id.value = otpauthParams.value.counter = generated_at.value = null
otpauthParams.value.service = otpauthParams.value.account = otpauthParams.value.icon = otpauthParams.value.otp_type = otpauthParams.value.secret = ''
password.value = '... ...'
next_password.value = ''
hasTOTP.value = false
clearTimeout(autoCloseTimeout.value)
@ -280,6 +294,7 @@
*/
function turnDotOn(dotIndex) {
dots.value.turnOn(dotIndex)
opacity.value = 'is-opacity-' + dotIndex
}
defineExpose({
@ -310,7 +325,7 @@
<p class="is-size-6 has-ellipsis" :class="mode == 'dark' ? 'has-text-grey' : 'has-text-grey-light'">{{ otpauthParams.account }}</p>
<p>
<span
v-if="!showInlineSpinner"
v-if="!showMainSpinner"
id="otp"
role="log"
ref="otpSpanTag"
@ -324,7 +339,7 @@
{{ useDisplayablePassword(password, user.preferences.showOtpAsDot && user.preferences.revealDottedOTP && revealPassword) }}
</span>
<span v-else tabindex="0" class="otp is-size-1">
<Spinner :isVisible="showInlineSpinner" :type="'raw'" />
<Spinner :isVisible="showMainSpinner" :type="'raw'" />
</span>
</p>
</UseColorMode>
@ -332,6 +347,20 @@
<p v-show="isHMacBased(otpauthParams.otp_type)">
{{ $t('twofaccounts.forms.counter.label') }}: {{ otpauthParams.counter }}
</p>
<p v-if="user.preferences.showNextOtp" class="mt-3 is-size-4">
<span
v-if="next_password"
class="is-clickable"
:class="opacity"
@click="copyOTP(next_password, true)"
@keyup.enter="copyOTP(next_password, true)"
:title="$t('commons.copy_next_password')"
>
{{ useDisplayablePassword(next_password, user.preferences.showOtpAsDot && user.preferences.revealDottedOTP && revealPassword) }}
</span>
<!-- <Spinner v-else-if="!showMainSpinner" :isVisible="true" :type="'raw'" /> -->
<span v-else>&nbsp;</span>
</p>
<p v-if="user.preferences.showOtpAsDot && user.preferences.revealDottedOTP" class="mt-3">
<button type="button" class="button is-ghost has-text-grey-dark" @click.stop="revealPassword = !revealPassword">
<font-awesome-icon v-if="revealPassword" :icon="['fas', 'eye']" />