diff --git a/src/lib/numeral.ts b/src/lib/numeral.ts index d6638c50..6917b9ff 100644 --- a/src/lib/numeral.ts +++ b/src/lib/numeral.ts @@ -285,15 +285,3 @@ export function getExchangedAmountByRate(amount: number, fromRate: string, toRat return amount * exchangeRate; } - -export function getConvertedAmount(baseAmount: number | '', fromExchangeRate: { rate: string }, toExchangeRate: { rate: string }): number | '' | null { - if (!fromExchangeRate || !toExchangeRate) { - return ''; - } - - if (baseAmount === '') { - return 0; - } - - return getExchangedAmountByRate(baseAmount as number, fromExchangeRate.rate, toExchangeRate.rate); -} diff --git a/src/locales/helper.js b/src/locales/helper.js index cb5be7b8..1fe443e9 100644 --- a/src/locales/helper.js +++ b/src/locales/helper.js @@ -47,7 +47,6 @@ import { appendDigitGroupingSymbol, parseAmount, formatAmount, - formatExchangeRateAmount, getAdaptiveDisplayAmountRate } from '@/lib/numeral.ts'; @@ -970,11 +969,6 @@ function getFormattedAmountWithCurrency(value, currencyCode, translateFn, userSt return appendCurrencySymbol(value, currencyDisplayType, currencyCode, currencyUnit, currencyName, isPlural); } -function getFormattedExchangeRateAmount(value, translateFn, userStore) { - const numberFormatOptions = getNumberFormatOptions(translateFn, userStore); - return formatExchangeRateAmount(value, numberFormatOptions); -} - function getAdaptiveAmountRate(amount1, amount2, fromExchangeRate, toExchangeRate, translateFn, userStore) { const numberFormatOptions = getNumberFormatOptions(translateFn, userStore); return getAdaptiveDisplayAmountRate(amount1, amount2, fromExchangeRate, toExchangeRate, numberFormatOptions); @@ -1538,7 +1532,6 @@ export function i18nFunctions(i18nGlobal) { parseAmount: (userStore, value) => getParsedAmountNumber(value, i18nGlobal.t, userStore), formatAmount: (userStore, value, currencyCode) => getFormattedAmount(value, i18nGlobal.t, userStore, currencyCode), formatAmountWithCurrency: (settingsStore, userStore, value, currencyCode) => getFormattedAmountWithCurrency(value, currencyCode, i18nGlobal.t, userStore, settingsStore), - formatExchangeRateAmount: (userStore, value) => getFormattedExchangeRateAmount(value, i18nGlobal.t, userStore), getAdaptiveAmountRate: (userStore, amount1, amount2, fromExchangeRate, toExchangeRate) => getAdaptiveAmountRate(amount1, amount2, fromExchangeRate, toExchangeRate, i18nGlobal.t, userStore), getAllExpenseAmountColors: () => getAllExpenseIncomeAmountColors(i18nGlobal.t, 1), getAllIncomeAmountColors: () => getAllExpenseIncomeAmountColors(i18nGlobal.t, 2), diff --git a/src/locales/helpers.ts b/src/locales/helpers.ts index 64c58bae..e06556a4 100644 --- a/src/locales/helpers.ts +++ b/src/locales/helpers.ts @@ -72,6 +72,8 @@ import type { ErrorResponse } from '@/core/api.ts'; import { ALL_CURRENCIES } from '@/consts/currency.ts'; import { KnownErrorCode, SPECIFIED_API_NOT_FOUND_ERRORS, PARAMETERIZED_ERRORS } from '@/consts/api.ts'; +import type { LatestExchangeRateResponse, LocalizedLatestExchangeRate } from '@/models/exchange_rate.ts'; + import { isObject, isString, @@ -104,6 +106,7 @@ import { import services from '@/lib/services.ts'; import logger from '@/lib/logger.ts'; +import { useSettingsStore } from '@/stores/setting.ts'; import { useUserStore } from '@/stores/user.ts'; export interface LocalizedErrorParameter { @@ -143,6 +146,7 @@ export function getI18nOptions(): object { export function useI18n() { const { t, locale } = useVueI18n(); + const settingsStore = useSettingsStore(); const userStore = useUserStore(); // private functions @@ -733,6 +737,49 @@ export function useI18n() { return ret; } + function getAllDisplayExchangeRates(exchangeRatesData?: LatestExchangeRateResponse): LocalizedLatestExchangeRate[] { + const availableExchangeRates: LocalizedLatestExchangeRate[] = []; + + if (!exchangeRatesData || !exchangeRatesData.exchangeRates) { + return availableExchangeRates; + } + + for (let i = 0; i < exchangeRatesData.exchangeRates.length; i++) { + const exchangeRate = exchangeRatesData.exchangeRates[i]; + + availableExchangeRates.push({ + currencyCode: exchangeRate.currency, + currencyDisplayName: getCurrencyName(exchangeRate.currency), + rate: exchangeRate.rate + }); + } + + if (settingsStore.appSettings.currencySortByInExchangeRatesPage === CurrencySortingType.Name.type) { + availableExchangeRates.sort(function(c1, c2) { + return c1.currencyDisplayName.localeCompare(c2.currencyDisplayName); + }); + } else if (settingsStore.appSettings.currencySortByInExchangeRatesPage === CurrencySortingType.CurrencyCode.type) { + availableExchangeRates.sort(function(c1, c2) { + return c1.currencyCode.localeCompare(c2.currencyCode); + }); + } else if (settingsStore.appSettings.currencySortByInExchangeRatesPage === CurrencySortingType.ExchangeRate.type) { + availableExchangeRates.sort(function(c1, c2) { + const rate1 = parseFloat(c1.rate); + const rate2 = parseFloat(c2.rate); + + if (rate1 > rate2) { + return 1; + } else if (rate1 < rate2) { + return -1; + } else { + return 0; + } + }); + } + + return availableExchangeRates; + } + function getMonthShortName(monthName: string): string { return t(`datetime.${monthName}.short`); } @@ -1083,6 +1130,7 @@ export function useI18n() { getAllTransactionEditScopeTypes: () => getLocalizedDisplayNameAndType(TransactionEditScopeType.values()), getAllTransactionTagFilterTypes: () => getLocalizedDisplayNameAndType(TransactionTagFilterType.values()), getAllTransactionScheduledFrequencyTypes: () => getLocalizedDisplayNameAndType(ScheduledTemplateFrequencyType.values()), + getAllDisplayExchangeRates, // get localized info getMonthShortName, getMonthLongName, diff --git a/src/models/exchange_rate.ts b/src/models/exchange_rate.ts index b203ecf5..1a1df03d 100644 --- a/src/models/exchange_rate.ts +++ b/src/models/exchange_rate.ts @@ -10,3 +10,9 @@ export interface LatestExchangeRateResponse { readonly baseCurrency: string; readonly exchangeRates: LatestExchangeRate[]; } + +export interface LocalizedLatestExchangeRate { + readonly currencyCode: string; + readonly currencyDisplayName: string; + readonly rate: string; +} diff --git a/src/views/base/ExchangeRatesPageBase.ts b/src/views/base/ExchangeRatesPageBase.ts new file mode 100644 index 00000000..7f3e7e17 --- /dev/null +++ b/src/views/base/ExchangeRatesPageBase.ts @@ -0,0 +1,65 @@ +import { ref, computed } from 'vue'; + +import { useI18n } from '@/locales/helpers.ts'; + +import { useUserStore } from '@/stores/user.ts'; +import { useExchangeRatesStore } from '@/stores/exchangeRates.ts'; + +import type { + LatestExchangeRate, + LatestExchangeRateResponse, + LocalizedLatestExchangeRate +} from '@/models/exchange_rate.ts'; + +import { getExchangedAmountByRate } from '@/lib/numeral.ts'; + +export function useExchangeRatesPageBase() { + const { getAllDisplayExchangeRates, formatUnixTimeToLongDate, parseAmount } = useI18n(); + + const userStore = useUserStore(); + const exchangeRatesStore = useExchangeRatesStore(); + + const baseCurrency = ref(userStore.currentUserDefaultCurrency); + const baseAmount = ref(100); + + const exchangeRatesData = computed(() => exchangeRatesStore.latestExchangeRates.data); + + const exchangeRatesDataUpdateTime = computed(() => { + const exchangeRatesLastUpdateTime = exchangeRatesStore.exchangeRatesLastUpdateTime; + return exchangeRatesLastUpdateTime ? formatUnixTimeToLongDate(exchangeRatesLastUpdateTime) : ''; + }); + + const availableExchangeRates = computed(() => { + return getAllDisplayExchangeRates(exchangeRatesData.value); + }); + + function getConvertedAmount(baseAmount: number | '', fromExchangeRate: LatestExchangeRate | LocalizedLatestExchangeRate, toExchangeRate: LatestExchangeRate | LocalizedLatestExchangeRate): number | '' | null { + if (!fromExchangeRate || !toExchangeRate) { + return ''; + } + + if (baseAmount === '') { + return 0; + } + + return getExchangedAmountByRate(baseAmount as number, fromExchangeRate.rate, toExchangeRate.rate); + } + + function setAsBaseline(currency: string, amount: string): void { + baseCurrency.value = currency; + baseAmount.value = parseAmount(amount); + } + + return { + // states + baseCurrency, + baseAmount, + // computed states + exchangeRatesData, + exchangeRatesDataUpdateTime, + availableExchangeRates, + // functions + getConvertedAmount, + setAsBaseline + }; +} diff --git a/src/views/desktop/ExchangeRatesPage.vue b/src/views/desktop/ExchangeRatesPage.vue index 7a148bf1..ba5ce1f6 100644 --- a/src/views/desktop/ExchangeRatesPage.vue +++ b/src/views/desktop/ExchangeRatesPage.vue @@ -5,16 +5,16 @@
- {{ $t('Data source') }} + {{ tt('Data source') }}

{{ exchangeRatesData.dataSource }} {{ exchangeRatesData.dataSource }} - {{ $t('None') }} + {{ tt('None') }}

- {{ $t('Last Updated') }} + {{ tt('Last Updated') }}

{{ exchangeRatesDataUpdateTime }} @@ -24,14 +24,14 @@

- {{ $t('Base Amount') }} + {{ tt('Base Amount') }}
- {{ $t('Base Currency') }} + {{ tt('Base Currency') }}
- {{ $t('None') }} + {{ tt('None') }} - {{ $t('Exchange Rates Data') }} + {{ tt('Exchange Rates Data') }} - {{ $t('Refresh') }} + {{ tt('Refresh') }}
@@ -81,9 +81,9 @@
- {{ $t('Currency') }} + {{ tt('Currency') }} - {{ $t('Amount') }} + {{ tt('Amount') }}
@@ -98,7 +98,7 @@ - {{ $t('No exchange rates data') }} + {{ tt('No exchange rates data') }} - {{ $t('Set as Base') }} + @click="setAsBaseline(exchangeRate.currencyCode, getFinalConvertedAmount(exchangeRate))"> + {{ tt('Set as Base') }} - {{ getConvertedAmount(exchangeRate) }} + {{ getFinalConvertedAmount(exchangeRate) }} @@ -133,132 +133,114 @@ -