@@ -5,9 +5,11 @@ import VueVirtualScroller from 'vue-virtual-scroller';
55import { setAssetPublicPath as setVueComponentsAssetPath } from '@nimiq/vue-components' ;
66// @ts -expect-error missing types for this package
77import VuePortal from '@linusborg/vue-simple-portal' ;
8+ import { init as initOasisApi } from '@nimiq/oasis-api' ;
89import { init as initFastspotApi } from '@nimiq/fastspot-api' ;
910
1011import App from './App.vue' ;
12+ import { serviceWorkerHasUpdate } from './registerServiceWorker' ;
1113import { launchNetwork } from './network' ;
1214import { useAccountStore } from './stores/Account' ;
1315import { dangerouslyInitializeDemo } from './lib/Demo' ;
@@ -26,6 +28,8 @@ import 'vue-virtual-scroller/dist/vue-virtual-scroller.css';
2628import '@/scss/themes.scss' ;
2729import { launchPolygon } from './ethers' ;
2830import { launchElectrum } from './electrum' ;
31+ import { useInactivityDetection } from './composables/useInactivityDetection' ;
32+ import { useFiatStore } from './stores/Fiat' ;
2933
3034// Set asset path relative to the public path defined in vue.config.json,
3135// see https://cli.vuejs.org/guide/mode-and-env.html#using-env-variables-in-client-side-code
@@ -40,6 +44,41 @@ Vue.use(VuePortal, { name: 'Portal' });
4044async function start ( ) {
4145 dangerouslyInitializeDemo ( router ) ;
4246
47+ serviceWorkerHasUpdate . then ( ( hasUpdate ) => useSettingsStore ( ) . state . updateAvailable = hasUpdate ) ;
48+
49+ // Update exchange rates every 2 minutes or every 10 minutes, depending on whether the Wallet is currently actively
50+ // used. If an update takes longer than that time due to a provider's rate limit, wait until the update succeeds
51+ // before queueing the next update. If the last update before page load was less than 2 minutes ago, wait the
52+ // remaining time first.
53+ const { timestamp : lastSuccessfulExchangeRateUpdate , updateExchangeRates } = useFiatStore ( ) ;
54+ const { isUserInactive } = useInactivityDetection ( ) ;
55+ let lastTriedExchangeRateUpdate = lastSuccessfulExchangeRateUpdate . value ;
56+ const TWO_MINUTES = 2 * 60 * 1000 ;
57+ const TEN_MINUTES = 5 * TWO_MINUTES ;
58+ let exchangeRateUpdateTimer = - 1 ;
59+ function queueExchangeRateUpdate ( ) {
60+ const interval = isUserInactive . value ? TEN_MINUTES : TWO_MINUTES ;
61+ // Update lastTriedExchangeRateUpdate as there might have been other exchange rate updates in the meantime, for
62+ // example on currency change.
63+ lastTriedExchangeRateUpdate = Math . max ( lastTriedExchangeRateUpdate , lastSuccessfulExchangeRateUpdate . value ) ;
64+ // Also set interval as upper bound to be immune to the user's system clock being wrong.
65+ const remainingTime = Math . max ( 0 , Math . min ( lastTriedExchangeRateUpdate + interval - Date . now ( ) , interval ) ) ;
66+ clearTimeout ( exchangeRateUpdateTimer ) ;
67+ exchangeRateUpdateTimer = window . setTimeout ( async ( ) => {
68+ // Silently ignore errors. If successful, this updates fiatStore.timestamp, which then also triggers price
69+ // chart updates in PriceChart.vue.
70+ await updateExchangeRates ( /* failGracefully */ true ) ;
71+ // In contrast to lastSuccessfulExchangeRateUpdate also update lastTriedExchangeRateUpdate on failed
72+ // attempts, to avoid repeated rescheduling on failure. Instead, simply skip the failed attempt and try
73+ // again at the regular interval. We update the time after the update attempt, instead of before it, because
74+ // exchange rates are up-to-date at the time an update successfully finishes, and get old from that point,
75+ // and not from the time the update was started.
76+ lastTriedExchangeRateUpdate = Date . now ( ) ;
77+ queueExchangeRateUpdate ( ) ;
78+ } , remainingTime ) ;
79+ }
80+ watch ( isUserInactive , queueExchangeRateUpdate ) ; // (Re)schedule exchange rate updates at the desired interval.
81+
4382 // Fetch language file
4483 const { language } = useSettingsStore ( ) ;
4584 loadLanguage ( language . value ) ;
@@ -52,6 +91,11 @@ async function start() {
5291 initFastspotApi ( config . fastspot . apiEndpoint , config . fastspot . apiKey ) ;
5392 } ) ;
5493
94+ watch ( ( ) => {
95+ if ( ! config . oasis . apiEndpoint ) return ;
96+ initOasisApi ( config . oasis . apiEndpoint ) ;
97+ } ) ;
98+
5599 config . demo . enabled = true ;
56100 // Make reactive config accessible in components
57101 Vue . prototype . $config = config ;
0 commit comments