diff --git a/src/Mobile.vue b/src/Mobile.vue index 10f44f60..bde587d6 100644 --- a/src/Mobile.vue +++ b/src/Mobile.vue @@ -68,7 +68,7 @@ export default { // auto refresh exchange rates data if (this.$settings.isAutoUpdateExchangeRatesData()) { - this.$services.autoRefreshLatestExchangeRates(); + this.$store.dispatch('getLatestExchangeRates', { silent: true, force: false }); } } } diff --git a/src/lib/exchangeRates.js b/src/lib/exchangeRates.js deleted file mode 100644 index eff2cd12..00000000 --- a/src/lib/exchangeRates.js +++ /dev/null @@ -1,54 +0,0 @@ -import utils from './utils.js'; - -const exchangeRatesLocalStorageKey = 'lab_app_exchange_rates'; - -function getExchangeRates() { - const storageData = localStorage.getItem(exchangeRatesLocalStorageKey) || '{}'; - return JSON.parse(storageData); -} - -function setExchangeRates(value) { - const storageData = JSON.stringify(value); - localStorage.setItem(exchangeRatesLocalStorageKey, storageData); -} - -function clearExchangeRates() { - localStorage.removeItem(exchangeRatesLocalStorageKey); -} - -function getExchangeRate(fromCurrency, toCurrency) { - const exchangeRates = getExchangeRates().exchangeRates; - const exchangeRateMap = {}; - - for (let i = 0; i < exchangeRates.length; i++) { - const exchangeRate = exchangeRates[i]; - exchangeRateMap[exchangeRate.currency] = exchangeRate; - } - - const fromCurrencyExchangeRate = exchangeRateMap[fromCurrency]; - const toCurrencyExchangeRate = exchangeRateMap[toCurrency]; - - if (!fromCurrencyExchangeRate || !toCurrencyExchangeRate) { - return null; - } - - return parseFloat(toCurrencyExchangeRate.rate) / parseFloat(fromCurrencyExchangeRate.rate); -} - -function getOtherCurrencyAmount(amount, fromCurrency, toCurrency) { - const exchangeRate = getExchangeRate(fromCurrency, toCurrency); - - if (!utils.isNumber(exchangeRate)) { - return null; - } - - return amount * exchangeRate; -} - -export default { - getExchangeRates, - setExchangeRates, - clearExchangeRates, - getExchangeRate, - getOtherCurrencyAmount, -}; diff --git a/src/lib/services.js b/src/lib/services.js index 114b482a..3dd91c56 100644 --- a/src/lib/services.js +++ b/src/lib/services.js @@ -1,7 +1,6 @@ import axios from 'axios'; -import moment from 'moment'; + import userState from "./userstate.js"; -import exchangeRates from "./exchangeRates.js"; const baseUrlPath = '/api'; @@ -330,26 +329,9 @@ export default { id }); }, - getLatestExchangeRates: () => { - return axios.get('v1/exchange_rates/latest.json'); - }, - autoRefreshLatestExchangeRates: () => { - const currentExchangeRateData = exchangeRates.getExchangeRates(); - - if (currentExchangeRateData && currentExchangeRateData.date === moment().format('YYYY-MM-DD')) { - return; - } - + getLatestExchangeRates: ({ ignoreError }) => { return axios.get('v1/exchange_rates/latest.json', { - ignoreError: true - }).then(response => { - const data = response.data; - - if (data && data.success && data.result && data.result.exchangeRates) { - exchangeRates.setExchangeRates(data.result); - } - - return data.result; + ignoreError: !!ignoreError }); }, }; diff --git a/src/mobile-main.js b/src/mobile-main.js index 71412cc2..50dd917b 100644 --- a/src/mobile-main.js +++ b/src/mobile-main.js @@ -28,7 +28,6 @@ import logger from './lib/logger.js'; import settings from './lib/settings.js'; import services from './lib/services.js'; import userstate from './lib/userstate.js'; -import exchangeRates from './lib/exchangeRates.js'; import webauthn from './lib/webauthn.js'; import utils from './lib/utils.js'; import stores from './store/index.js'; @@ -211,7 +210,6 @@ Vue.prototype.$hideLoading = function () { }; Vue.prototype.$services = services; -Vue.prototype.$exchangeRates = exchangeRates; Vue.prototype.$user = userstate; Vue.filter('itemFieldContent', (value, fieldName, defaultValue, translate) => itemFieldContentFilter({ i18n }, value, fieldName, defaultValue, translate)); diff --git a/src/store/exchangeRates.js b/src/store/exchangeRates.js new file mode 100644 index 00000000..956a40bf --- /dev/null +++ b/src/store/exchangeRates.js @@ -0,0 +1,115 @@ +import services from '../lib/services.js'; +import logger from '../lib/logger.js'; +import utils from '../lib/utils.js'; + +import { + STORE_LATEST_EXCHANGE_RATES +} from './mutations.js'; + +const exchangeRatesLocalStorageKey = 'lab_app_exchange_rates'; + +function getLatestExchangeRates(context, { silent, force }) { + const currentExchangeRateData = context.state.latestExchangeRates; + const now = new Date(); + + if (!force) { + if (currentExchangeRateData && currentExchangeRateData.time && currentExchangeRateData.data && + currentExchangeRateData.data.date === utils.formatDate(now, 'YYYY-MM-DD')) { + return currentExchangeRateData.data; + } + + if (currentExchangeRateData && currentExchangeRateData.time && currentExchangeRateData.data && + utils.formatUnixTime(currentExchangeRateData.time, 'YYYY-MM-DD HH') === utils.formatDate(now, 'YYYY-MM-DD HH')) { + return currentExchangeRateData.data; + } + } + + return new Promise((resolve, reject) => { + services.getLatestExchangeRates({ + ignoreError: silent + }).then(response => { + const data = response.data; + + if (!data || !data.success || !data.result) { + reject({ message: 'Unable to get exchange rates data' }); + return; + } + + context.commit(STORE_LATEST_EXCHANGE_RATES, { + time: utils.getUnixTime(now), + data: data.result + }); + + resolve(data.result); + }).catch(error => { + logger.error('failed to get latest exchange rates data', error); + + if (error && error.processed) { + reject(error); + } else if (error.response && error.response.data && error.response.data.errorMessage) { + reject({ error: error.response.data }); + } else { + reject({ message: 'Unable to get exchange rates data' }); + } + }); + }); +} + +function exchangeRatesLastUpdateDate(state) { + const exchangeRates = state.latestExchangeRates || {}; + return exchangeRates && exchangeRates.data ? exchangeRates.data.date : null; +} + +function getExchangedAmount(state) { + return (amount, fromCurrency, toCurrency) => { + if (!state.latestExchangeRates || !state.latestExchangeRates.data || !state.latestExchangeRates.data.exchangeRates) { + return null; + } + + const exchangeRates = state.latestExchangeRates.data.exchangeRates; + const exchangeRateMap = {}; + + for (let i = 0; i < exchangeRates.length; i++) { + const exchangeRate = exchangeRates[i]; + exchangeRateMap[exchangeRate.currency] = exchangeRate; + } + + const fromCurrencyExchangeRate = exchangeRateMap[fromCurrency]; + const toCurrencyExchangeRate = exchangeRateMap[toCurrency]; + + if (!fromCurrencyExchangeRate || !toCurrencyExchangeRate) { + return null; + } + + const exchangeRate = parseFloat(toCurrencyExchangeRate.rate) / parseFloat(fromCurrencyExchangeRate.rate); + + if (!utils.isNumber(exchangeRate)) { + return null; + } + + return amount * exchangeRate; + }; +} + +function getExchangeRatesFromLocalStorage() { + const storageData = localStorage.getItem(exchangeRatesLocalStorageKey) || '{}'; + return JSON.parse(storageData); +} + +function setExchangeRatesToLocalStorage(value) { + const storageData = JSON.stringify(value); + localStorage.setItem(exchangeRatesLocalStorageKey, storageData); +} + +function clearExchangeRatesFromLocalStorage() { + localStorage.removeItem(exchangeRatesLocalStorageKey); +} + +export default { + getLatestExchangeRates, + exchangeRatesLastUpdateDate, + getExchangedAmount, + getExchangeRatesFromLocalStorage, + setExchangeRatesToLocalStorage, + clearExchangeRatesFromLocalStorage, +} diff --git a/src/store/index.js b/src/store/index.js index 449980fb..c87a2347 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -7,6 +7,8 @@ import { STORE_USER_INFO, CLEAR_USER_INFO, + STORE_LATEST_EXCHANGE_RATES, + LOAD_ACCOUNT_LIST, ADD_ACCOUNT_TO_ACCOUNT_LIST, SAVE_ACCOUNT_IN_ACCOUNT_LIST, @@ -28,6 +30,7 @@ import { import user from './user.js'; import token from './token.js'; +import exchangeRates from './exchangeRates.js'; import account from './account.js'; import transactionTag from './transactionTag.js'; @@ -35,6 +38,7 @@ const stores = { strict: process.env.NODE_ENV !== 'production', state: { currentUserInfo: userState.getUserInfo(), + latestExchangeRates: exchangeRates.getExchangeRatesFromLocalStorage(), allAccounts: [], allAccountsMap: {}, allCategorizedAccounts: {}, @@ -49,11 +53,14 @@ const stores = { getters: { currentUserNickname: user.currentUserNickname, currentUserDefaultCurrency: user.currentUserDefaultCurrency, + exchangeRatesLastUpdateDate: exchangeRates.exchangeRatesLastUpdateDate, + getExchangedAmount: exchangeRates.getExchangedAmount, allAvailableAccountsCount: account.allAvailableAccountsCount, allVisibleAccountsCount: account.allVisibleAccountsCount, }, mutations: { [RESET_STATE] (state) { + state.latestExchangeRates = {}; state.allAccounts = []; state.allAccountsMap = {}; state.allCategorizedAccounts = {}; @@ -64,6 +71,8 @@ const stores = { state.allTransactionTagsMap = {}; state.transactionTagListStateInvalid = true; state.transactions = []; + + exchangeRates.clearExchangeRatesFromLocalStorage(); }, [STORE_USER_INFO] (state, userInfo) { state.currentUserInfo = userInfo; @@ -73,6 +82,10 @@ const stores = { state.currentUserInfo = null; userState.clearUserInfo(); }, + [STORE_LATEST_EXCHANGE_RATES] (state, latestExchangeRates) { + state.latestExchangeRates = latestExchangeRates; + exchangeRates.setExchangeRatesToLocalStorage(latestExchangeRates); + }, [LOAD_ACCOUNT_LIST] (state, accounts) { state.allAccounts = accounts; state.allAccountsMap = {}; @@ -250,10 +263,12 @@ const stores = { getCurrentUserProfile: user.getCurrentUserProfile, updateUserProfile: user.updateUserProfile, clearUserInfoState: user.clearUserInfoState, + resetState: user.resetState, getAllTokens: token.getAllTokens, refreshTokenAndRevokeOldToken: token.refreshTokenAndRevokeOldToken, revokeToken: token.revokeToken, revokeAllTokens: token.revokeAllTokens, + getLatestExchangeRates: exchangeRates.getLatestExchangeRates, loadAllAccounts: account.loadAllAccounts, saveAccount: account.saveAccount, getAccount: account.getAccount, diff --git a/src/store/mutations.js b/src/store/mutations.js index 8f77afa4..b4e04373 100644 --- a/src/store/mutations.js +++ b/src/store/mutations.js @@ -3,6 +3,8 @@ export const RESET_STATE = 'RESET_STATE'; export const STORE_USER_INFO = 'STORE_USER_INFO'; export const CLEAR_USER_INFO = 'CLEAR_USER_INFO'; +export const STORE_LATEST_EXCHANGE_RATES = 'STORE_LATEST_EXCHANGE_RATES'; + export const LOAD_ACCOUNT_LIST = 'LOAD_ACCOUNT_LIST'; export const ADD_ACCOUNT_TO_ACCOUNT_LIST = 'ADD_ACCOUNT_TO_ACCOUNT_LIST'; export const SAVE_ACCOUNT_IN_ACCOUNT_LIST = 'SAVE_ACCOUNT_IN_ACCOUNT_LIST'; diff --git a/src/store/user.js b/src/store/user.js index e050bc2a..01650b47 100644 --- a/src/store/user.js +++ b/src/store/user.js @@ -264,6 +264,10 @@ function clearUserInfoState(context) { context.commit(CLEAR_USER_INFO); } +function resetState(context) { + context.commit(RESET_STATE); +} + function currentUserNickname(state) { const userInfo = state.currentUserInfo || {}; return userInfo.nickname || userInfo.username || null; @@ -282,6 +286,7 @@ export default { getCurrentUserProfile, updateUserProfile, clearUserInfoState, + resetState, currentUserNickname, currentUserDefaultCurrency } diff --git a/src/views/mobile/ExchangeRates.vue b/src/views/mobile/ExchangeRates.vue index a0e9e7ed..c3adb8a9 100644 --- a/src/views/mobile/ExchangeRates.vue +++ b/src/views/mobile/ExchangeRates.vue @@ -9,7 +9,7 @@ - + - + - + { + self.$store.dispatch('getLatestExchangeRates', { + silent: false, + force: true + }).then(() => { if (done) { done(); } @@ -125,20 +130,8 @@ export default { self.updating = false; self.$hideLoading(); - const data = response.data; - - if (!data || !data.success || !data.result) { - self.$toast('Unable to get exchange rates data'); - return; - } - - self.exchangeRatesData = data.result; - self.$exchangeRates.setExchangeRates(data.result); - self.$toast('Exchange rates data has been updated'); }).catch(error => { - self.$logger.error('failed to get latest exchange rates data', error); - if (done) { done(); } @@ -146,10 +139,8 @@ export default { self.updating = false; self.$hideLoading(); - if (error.response && error.response.data && error.response.data.errorMessage) { - self.$toast({ error: error.response.data }); - } else if (!error.processed) { - self.$toast('Unable to get exchange rates data'); + if (!error.processed) { + self.$toast(error.message || error); } }); } diff --git a/src/views/mobile/Login.vue b/src/views/mobile/Login.vue index aa6999c2..eb777884 100644 --- a/src/views/mobile/Login.vue +++ b/src/views/mobile/Login.vue @@ -188,7 +188,7 @@ export default { } if (self.$settings.isAutoUpdateExchangeRatesData()) { - self.$services.autoRefreshLatestExchangeRates(); + self.$store.dispatch('getLatestExchangeRates', { silent: true, force: false }); } router.refreshPage(); @@ -239,7 +239,7 @@ export default { self.$hideLoading(); if (self.$settings.isAutoUpdateExchangeRatesData()) { - self.$services.autoRefreshLatestExchangeRates(); + self.$store.dispatch('getLatestExchangeRates', { silent: true, force: false }); } self.show2faSheet = false; diff --git a/src/views/mobile/Settings.vue b/src/views/mobile/Settings.vue index efccf007..b04baf73 100644 --- a/src/views/mobile/Settings.vue +++ b/src/views/mobile/Settings.vue @@ -86,8 +86,7 @@ export default { const self = this; return { - isEnableApplicationLock: this.$settings.isEnableApplicationLock(), - exchangeRatesLastUpdateDate: self.getExchangeRatesLastUpdateDate(), + isEnableApplicationLock: self.$settings.isEnableApplicationLock(), logouting: false }; }, @@ -104,7 +103,6 @@ export default { }, set: function (value) { this.$locale.setLanguage(value); - this.exchangeRatesLastUpdateDate = this.getExchangeRatesLastUpdateDate(); } }, currentNickName() { @@ -113,6 +111,10 @@ export default { isDataExportingEnabled() { return this.$settings.isDataExportingEnabled(); }, + exchangeRatesLastUpdateDate() { + const exchangeRatesLastUpdateDate = this.$store.getters.exchangeRatesLastUpdateDate; + return exchangeRatesLastUpdateDate ? this.$utilities.formatDate(exchangeRatesLastUpdateDate, this.$t('format.date.long')) : ''; + }, isAutoUpdateExchangeRatesData: { get: function () { return this.$settings.isAutoUpdateExchangeRatesData(); @@ -171,11 +173,6 @@ export default { methods: { onPageAfterIn() { this.isEnableApplicationLock = this.$settings.isEnableApplicationLock(); - this.exchangeRatesLastUpdateDate = this.getExchangeRatesLastUpdateDate(); - }, - getExchangeRatesLastUpdateDate() { - const exchangeRates = this.$exchangeRates.getExchangeRates(); - return exchangeRates && exchangeRates.date ? this.$moment(exchangeRates.date).format(this.$t('format.date.long')) : ''; }, logout() { const self = this; @@ -189,7 +186,6 @@ export default { self.logouting = false; self.$hideLoading(); - self.$exchangeRates.clearExchangeRates(); self.$settings.clearSettings(); self.$locale.init(); diff --git a/src/views/mobile/Signup.vue b/src/views/mobile/Signup.vue index 062d0897..3359cefa 100644 --- a/src/views/mobile/Signup.vue +++ b/src/views/mobile/Signup.vue @@ -160,7 +160,7 @@ export default { if (self.$user.isUserLogined()) { if (self.$settings.isAutoUpdateExchangeRatesData()) { - self.$services.autoRefreshLatestExchangeRates(); + self.$store.dispatch('getLatestExchangeRates', { silent: true, force: false }); } } diff --git a/src/views/mobile/Unlock.vue b/src/views/mobile/Unlock.vue index 1ccd9d5a..df729e1f 100644 --- a/src/views/mobile/Unlock.vue +++ b/src/views/mobile/Unlock.vue @@ -60,7 +60,7 @@ export default { self.$store.dispatch('refreshTokenAndRevokeOldToken'); if (self.$settings.isAutoUpdateExchangeRatesData()) { - self.$services.autoRefreshLatestExchangeRates(); + self.$store.dispatch('getLatestExchangeRates', { silent: true, force: false }); } router.refreshPage(); @@ -104,7 +104,7 @@ export default { this.$store.dispatch('refreshTokenAndRevokeOldToken'); if (this.$settings.isAutoUpdateExchangeRatesData()) { - this.$services.autoRefreshLatestExchangeRates(); + this.$store.dispatch('getLatestExchangeRates', { silent: true, force: false }); } router.refreshPage(); @@ -121,7 +121,7 @@ export default { self.$user.clearTokenAndUserInfo(true); self.$user.clearWebAuthnConfig(); self.$store.dispatch('clearUserInfoState'); - self.$exchangeRates.clearExchangeRates(); + self.$store.dispatch('resetState'); self.$settings.clearSettings(); self.$locale.init(); diff --git a/src/views/mobile/accounts/List.vue b/src/views/mobile/accounts/List.vue index 5303a01a..1e764cee 100644 --- a/src/views/mobile/accounts/List.vue +++ b/src/views/mobile/accounts/List.vue @@ -245,7 +245,7 @@ export default { if (accountsBalance[i].currency === this.defaultCurrency) { netAssets += accountsBalance[i].balance; } else { - const balance = this.$exchangeRates.getOtherCurrencyAmount(accountsBalance[i].balance, accountsBalance[i].currency, this.defaultCurrency); + const balance = this.$store.getters.getExchangedAmount(accountsBalance[i].balance, accountsBalance[i].currency, this.defaultCurrency); if (!this.$utilities.isNumber(balance)) { hasUnCalculatedAmount = true; @@ -275,7 +275,7 @@ export default { if (accountsBalance[i].currency === this.defaultCurrency) { totalAssets += accountsBalance[i].balance; } else { - const balance = this.$exchangeRates.getOtherCurrencyAmount(accountsBalance[i].balance, accountsBalance[i].currency, this.defaultCurrency); + const balance = this.$store.getters.getExchangedAmount(accountsBalance[i].balance, accountsBalance[i].currency, this.defaultCurrency); if (!this.$utilities.isNumber(balance)) { hasUnCalculatedAmount = true; @@ -305,7 +305,7 @@ export default { if (accountsBalance[i].currency === this.defaultCurrency) { totalLiabilities -= accountsBalance[i].balance; } else { - const balance = this.$exchangeRates.getOtherCurrencyAmount(accountsBalance[i].balance, accountsBalance[i].currency, this.defaultCurrency); + const balance = this.$store.getters.getExchangedAmount(accountsBalance[i].balance, accountsBalance[i].currency, this.defaultCurrency); if (!this.$utilities.isNumber(balance)) { hasUnCalculatedAmount = true; @@ -431,7 +431,7 @@ export default { totalBalance += accountsBalance[i].balance; } } else { - const balance = this.$exchangeRates.getOtherCurrencyAmount(accountsBalance[i].balance, accountsBalance[i].currency, this.defaultCurrency); + const balance = this.$store.getters.getExchangedAmount(accountsBalance[i].balance, accountsBalance[i].currency, this.defaultCurrency); if (!this.$utilities.isNumber(balance)) { hasUnCalculatedAmount = true; diff --git a/src/views/mobile/transactions/Edit.vue b/src/views/mobile/transactions/Edit.vue index 0326c9e7..a2ca605c 100644 --- a/src/views/mobile/transactions/Edit.vue +++ b/src/views/mobile/transactions/Edit.vue @@ -369,8 +369,8 @@ export default { } if (sourceAccount && destinationAccount && sourceAccount.currency !== destinationAccount.currency) { - const exchangedOldValue = this.$exchangeRates.getOtherCurrencyAmount(oldValue, sourceAccount.currency, destinationAccount.currency); - const exchangedNewValue = this.$exchangeRates.getOtherCurrencyAmount(newValue, sourceAccount.currency, destinationAccount.currency); + const exchangedOldValue = this.$store.getters.getExchangedAmount(oldValue, sourceAccount.currency, destinationAccount.currency); + const exchangedNewValue = this.$store.getters.getExchangedAmount(newValue, sourceAccount.currency, destinationAccount.currency); if (this.$utilities.isNumber(exchangedOldValue)) { oldValue = Math.floor(exchangedOldValue); @@ -704,7 +704,7 @@ export default { totalBalance += accountsBalance[i].balance; } } else { - const balance = this.$exchangeRates.getOtherCurrencyAmount(accountsBalance[i].balance, accountsBalance[i].currency, this.defaultCurrency); + const balance = this.$store.getters.getExchangedAmount(accountsBalance[i].balance, accountsBalance[i].currency, this.defaultCurrency); if (!this.$utilities.isNumber(balance)) { hasUnCalculatedAmount = true; diff --git a/src/views/mobile/transactions/List.vue b/src/views/mobile/transactions/List.vue index ab777cb3..c0bb3cd4 100644 --- a/src/views/mobile/transactions/List.vue +++ b/src/views/mobile/transactions/List.vue @@ -888,7 +888,7 @@ export default { let amount = transaction.sourceAmount; if (transaction.sourceAccount.currency !== this.defaultCurrency) { - const balance = this.$exchangeRates.getOtherCurrencyAmount(amount, transaction.sourceAccount.currency, this.defaultCurrency); + const balance = this.$store.getters.getExchangedAmount(amount, transaction.sourceAccount.currency, this.defaultCurrency); if (!this.$utilities.isNumber(balance)) { if (transaction.type === this.$constants.transaction.allTransactionTypes.Expense) {