From e8a3c441be8f46df1c67aaef26cbe5ffca04347d Mon Sep 17 00:00:00 2001 From: Bubka <858858+Bubka@users.noreply.github.com> Date: Fri, 17 Nov 2023 19:49:39 +0100 Subject: [PATCH] Fix Always On OTPs & Dots display and looping --- .../js_vue3/services/twofaccountService.js | 8 +- resources/js_vue3/stores/twofaccounts.js | 32 +++-- .../js_vue3/views/twofaccounts/Accounts.vue | 131 ++++++++++-------- 3 files changed, 96 insertions(+), 75 deletions(-) diff --git a/resources/js_vue3/services/twofaccountService.js b/resources/js_vue3/services/twofaccountService.js index 532e6d67..f8ed4b6f 100644 --- a/resources/js_vue3/services/twofaccountService.js +++ b/resources/js_vue3/services/twofaccountService.js @@ -3,12 +3,12 @@ import { httpClientFactory } from '@/services/httpClientFactory' const apiClient = httpClientFactory('api') export default { - getAll(withOtp = false) { - return apiClient.get('/twofaccounts' + (withOtp ? '?withOtp=1' : '')) + getAll(withOtp = false, config = {}) { + return apiClient.get('/twofaccounts' + (withOtp ? '?withOtp=1' : ''), { ...config }) }, - getByIds(ids, withOtp = false) { - return apiClient.get('/twofaccounts?ids=' + ids + (withOtp ? '&withOtp=1' : '')) + getByIds(ids, withOtp = false, config = {}) { + return apiClient.get('/twofaccounts?ids=' + ids + (withOtp ? '&withOtp=1' : ''), { ...config }) }, get(id, config = {}) { diff --git a/resources/js_vue3/stores/twofaccounts.js b/resources/js_vue3/stores/twofaccounts.js index a5d3be45..daa29e55 100644 --- a/resources/js_vue3/stores/twofaccounts.js +++ b/resources/js_vue3/stores/twofaccounts.js @@ -79,31 +79,33 @@ export const useTwofaccounts = defineStore({ /** * Refreshes the accounts collection using the backend */ - async fetch() { + async fetch(force = false) { // We do not want to fetch fresh data multiple times in the same 2s timespan const age = Math.floor(Date.now() - this.fetchedOn) - const isNotFresh = age > 2000 + const isOutOfAge = age > 2000 - if (isNotFresh) { + if (isOutOfAge || force) { this.fetchedOn = Date.now() await twofaccountService.getAll(! useUserStore().preferences.getOtpOnRequest).then(response => { // Defines if the store was up-to-date with the backend - this.backendWasNewer = response.data.length !== this.items.length - - this.items.forEach((item) => { - let matchingBackendItem = response.data.find(e => e.id === item.id) - if (matchingBackendItem == undefined) { - this.backendWasNewer = true - return; - } - for (const field in item) { - if (field !== 'otp' && item[field] != matchingBackendItem[field]) { + if (force) { + this.backendWasNewer = response.data.length !== this.items.length + + this.items.forEach((item) => { + let matchingBackendItem = response.data.find(e => e.id === item.id) + if (matchingBackendItem == undefined) { this.backendWasNewer = true return; } - } - }) + for (const field in item) { + if (field !== 'otp' && item[field] != matchingBackendItem[field]) { + this.backendWasNewer = true + return; + } + } + }) + } // Updates the state this.items = response.data diff --git a/resources/js_vue3/views/twofaccounts/Accounts.vue b/resources/js_vue3/views/twofaccounts/Accounts.vue index 22b9f159..007abbd1 100644 --- a/resources/js_vue3/views/twofaccounts/Accounts.vue +++ b/resources/js_vue3/views/twofaccounts/Accounts.vue @@ -32,9 +32,8 @@ const showGroupSwitch = ref(false) const showDestinationGroupSelector = ref(false) const isDragging = ref(false) - const stepIndexesCache = ref({}) const isRenewingOTPs = ref(false) - const renewedOTPs = ref(null) + const renewedPeriod = ref(null) const otpDisplay = ref(null) const otpDisplayProps = ref({ @@ -85,12 +84,17 @@ // This SFC is reached only if the user has some twofaccounts (see the starter middleware). // This allows to display accounts without latency. // - // We sync the store with the backend again to - twofaccounts.fetch().then(() => { - if (twofaccounts.backendWasNewer) { - notify.info({ text: trans('commons.data_refreshed_to_reflect_server_changes'), duration: 10000 }) - } - }) + // We sync the store with the backend again to + if (! user.preferences.getOtpOnRequest) { + updateTotps() + } + else { + twofaccounts.fetch().then(() => { + if (twofaccounts.backendWasNewer) { + notify.info({ text: trans('commons.data_refreshed_to_reflect_server_changes'), duration: 10000 }) + } + }) + } groups.fetch() }) @@ -207,59 +211,69 @@ twofaccounts.saveOrder() } - - /** - * Turns dots On at the current step and caches the state - */ - function setCurrentStep(period, stepIndex) { - stepIndexesCache.value[period] = stepIndex - turnDotsOn(period, stepIndex) - } - - /** - * Turns dots On at the cached step index - */ - function turnDotsOnFromCache(period, stepIndex) { - if (stepIndexesCache.value[period] != undefined) { - turnDotsOn(period, stepIndexesCache.value[period]) - } - } - /** * Turns dots On for all dots components that match the provided period */ function turnDotsOn(period, stepIndex) { dotsRefs.value - .filter((dots) => dots.props.period == period) + .filter((dots) => dots.props.period == period || period == undefined) .forEach((dot) => { dot.turnOn(stepIndex) }) } /** - * Updates "Always On" OTPs for all TOTP accounts with the given period and restarts loopers + * Turns dots Off for all dots components that match the provided period + */ + function turnDotsOff(period) { + dotsRefs.value + .filter((dots) => dots.props.period == period || period == undefined) + .forEach((dot) => { + dot.turnOff() + }) + } + + /** + * Updates "Always On" OTPs for all TOTP accounts and (re)starts loopers */ async function updateTotps(period) { isRenewingOTPs.value = true - renewedOTPs.value = period - - twofaccountService.getByIds(twofaccounts.accountIdsWithPeriod(period).join(','), true).then(response => { + turnDotsOff(period) + let fetchPromise + + if (period == undefined) { + renewedPeriod.value = -1 + fetchPromise = twofaccountService.getAll(true) + } else { + renewedPeriod.value = period + fetchPromise = twofaccountService.getByIds(twofaccounts.accountIdsWithPeriod(period).join(','), true) + } + + fetchPromise.then(response => { + let generatedAt = 0 + + // twofaccounts OTP updates response.data.forEach((account) => { const index = twofaccounts.items.findIndex(acc => acc.id === account.id) - twofaccounts.items[index].otp = account.otp - - looperRefs.value.forEach((looper) => { - if (looper.props.period == period) { - nextTick().then(() => { - looper.startLoop(account.otp.generated_at) - }) - } - }) + if (twofaccounts.items[index] == undefined) { + twofaccounts.items.push(account) + } + else twofaccounts.items[index].otp = account.otp + generatedAt = account.otp.generated_at + }) + + // Loopers restart at new timestamp + looperRefs.value.forEach((looper) => { + if (looper.props.period == period || period == undefined) { + nextTick().then(() => { + looper.startLoop(generatedAt) + }) + } }) }) .finally(() => { isRenewingOTPs.value = false - renewedOTPs.value = null + renewedPeriod.value = null }) } @@ -339,6 +353,20 @@ @please-close-me="showOtpInModal = false"> + + + +
@@ -366,13 +394,17 @@
- + {{ useDisplayablePassword(account.otp.password) }} - + @@ -415,18 +447,5 @@
- - - -