diff --git a/__mocks__/react-native-localize.ts b/__mocks__/react-native-localize.ts new file mode 100644 index 000000000..18340ba9c --- /dev/null +++ b/__mocks__/react-native-localize.ts @@ -0,0 +1 @@ +export * from "react-native-localize/mock/jest"; diff --git a/__tests__/fiat.test.ts b/__tests__/fiat.test.ts index e81a234df..ce90e94d8 100644 --- a/__tests__/fiat.test.ts +++ b/__tests__/fiat.test.ts @@ -71,7 +71,7 @@ describe('Pulls latest fiat exchange rates and checks the wallet store for valid }); it('Blocktank FX rates with default selected currency', async () => { - const res = await updateExchangeRates({}); + const res = await updateExchangeRates(); expect(res.isOk()).toEqual(true); if (res.isErr()) { @@ -155,7 +155,7 @@ describe('Pulls latest fiat exchange rates and checks the wallet store for valid describe('convertToSats', () => { describe('can work with exchange rates', () => { beforeAll(async () => { - const res = await updateExchangeRates({}); + const res = await updateExchangeRates(); if (res.isErr()) { throw res.error; } diff --git a/e2e/settings.e2e.js b/e2e/settings.e2e.js index 0a0536e56..1fef292ef 100644 --- a/e2e/settings.e2e.js +++ b/e2e/settings.e2e.js @@ -535,14 +535,14 @@ d('Settings', () => { by.id('ConnectedUrl'), ).getAttributes(); - const alteredRelay = origRelay + '/'; + const alteredRelay = `${origRelay}/`; await element(by.id('UrlInput')).replaceText(alteredRelay); await element(by.id('WebRelayStatus')).tap(); // close keyboard await element(by.id('ConnectToUrl')).tap(); await sleep(1000); // url should be updated - let { label: newRelay } = await element( + const { label: newRelay } = await element( by.id('ConnectedUrl'), ).getAttributes(); @@ -570,14 +570,14 @@ d('Settings', () => { ).getAttributes(); // add slash at the end - const newUrl = origValue + '/'; + const newUrl = `${origValue}/`; await element(by.id('RGSUrl')).replaceText(newUrl); await element(by.id('RGSUrl')).tapReturnKey(); await element(by.id('ConnectToHost')).tap(); await sleep(1000); // url should be updated - let { label: newValue } = await element( + const { label: newValue } = await element( by.id('ConnectedUrl'), ).getAttributes(); @@ -587,7 +587,7 @@ d('Settings', () => { await element(by.id('ResetToDefault')).tap(); await element(by.id('ConnectToHost')).tap(); - let { label: resetValue } = await element( + const { label: resetValue } = await element( by.id('ConnectedUrl'), ).getAttributes(); diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 3aed02a62..6bccb4377 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1782,7 +1782,7 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - RNLocalize (3.0.2): + - RNLocalize (3.4.1): - React-Core - RNNotifee (9.1.8): - React-Core @@ -2391,7 +2391,7 @@ SPEC CHECKSUMS: RNFS: 89de7d7f4c0f6bafa05343c578f61118c8282ed8 RNGestureHandler: 9f3109e11ed88fe5bed280bf7762b25e4c52f396 RNKeychain: 824e7372c7e921d76c4ea4169bdc6c7a32025784 - RNLocalize: 01b15a24a8f1856eeaeeb77f4179ccd88c117a73 + RNLocalize: 15463c4d79c7da45230064b4adcf5e9bb984667e RNNotifee: 5e3b271e8ea7456a36eec994085543c9adca9168 RNPermissions: a03f8bd0e6c9d87597e39be694f90d208dd6f835 RNQrGenerator: afacf12b55dfba0e3aaca963eec23691e8426431 diff --git a/jest.setup.js b/jest.setup.js index 8558c3ef0..e1f55e0c0 100644 --- a/jest.setup.js +++ b/jest.setup.js @@ -1,6 +1,5 @@ import mockRNDeviceInfo from 'react-native-device-info/jest/react-native-device-info-mock'; import mockRNCNetInfo from '@react-native-community/netinfo/jest/netinfo-mock'; -import mockRNLocalize from 'react-native-localize/mock'; import * as mockLDK from '@synonymdev/react-native-ldk/dist/mock'; jest.mock('react-native-reanimated', () => @@ -12,7 +11,6 @@ jest.mock('react-native-permissions', () => require('react-native-permissions/mock'), ); jest.mock('@react-native-community/netinfo', () => mockRNCNetInfo); -jest.mock('react-native-localize', () => mockRNLocalize); jest.mock('@synonymdev/react-native-ldk', () => mockLDK); global.net = require('net'); // needed by Electrum client. For RN it is proviced in shim.js diff --git a/package.json b/package.json index 8daabff2f..67b1a46d7 100644 --- a/package.json +++ b/package.json @@ -86,7 +86,7 @@ "react-native-image-picker": "8.1.0", "react-native-keyboard-accessory": "0.1.16", "react-native-keychain": "9.2.3", - "react-native-localize": "3.0.2", + "react-native-localize": "3.4.1", "react-native-mmkv": "2.12.2", "react-native-modal": "13.0.1", "react-native-permissions": "5.2.5", diff --git a/src/screens/Settings/Currencies/index.tsx b/src/screens/Settings/Currencies/index.tsx index 33b8955dc..357ed45d5 100644 --- a/src/screens/Settings/Currencies/index.tsx +++ b/src/screens/Settings/Currencies/index.tsx @@ -19,6 +19,8 @@ const CurrenciesSettings = ({ const selectedCurrency = useAppSelector(selectedCurrencySelector); const currencyListData: IListData[] = useMemo(() => { + const currencies = Object.keys(exchangeRates).sort(); + const onSetCurrency = (currency: string): void => { dispatch(updateSettings({ selectedCurrency: currency })); }; @@ -40,17 +42,15 @@ const CurrenciesSettings = ({ }, { title: t('general.currency_other'), - data: Object.keys(exchangeRates) - .sort() - .map((ticker) => ({ - title: ticker, - value: selectedCurrency === ticker, - type: EItemType.button, - onPress: (): void => { - navigation.goBack(); - onSetCurrency(ticker); - }, - })), + data: currencies.map((ticker) => ({ + title: ticker, + value: selectedCurrency === ticker, + type: EItemType.button, + onPress: (): void => { + navigation.goBack(); + onSetCurrency(ticker); + }, + })), }, ]; }, [selectedCurrency, exchangeRates, navigation, t, dispatch]); diff --git a/src/store/shapes/settings.ts b/src/store/shapes/settings.ts index 5f721be02..870872641 100644 --- a/src/store/shapes/settings.ts +++ b/src/store/shapes/settings.ts @@ -1,18 +1,36 @@ import { ECoinSelectPreference, EProtocol, TServer } from 'beignet'; import cloneDeep from 'lodash/cloneDeep'; +import { getCurrencies } from 'react-native-localize'; import { + __E2E__, __ELECTRUM_REGTEST_HOST__, __ELECTRUM_REGTEST_PROTO__, __ELECTRUM_REGTEST_SSL_PORT__, __ELECTRUM_REGTEST_TCP_PORT__, __WEB_RELAY__, } from '../../constants/env'; +import { currencies } from '../../utils/exchange-rate/currencies'; import { EAvailableNetwork } from '../../utils/networks'; import { TSettings } from '../slices/settings'; import { ETransactionSpeed } from '../types/settings'; import { EDenomination, EUnit } from '../types/wallet'; +const getDefaultCurrency = (): string => { + if (__E2E__) { + return 'USD'; + } + + const localCurrencies = getCurrencies(); + + // Find the first currency that matches the user's preference + const preferredCurrency = localCurrencies.find((currency) => { + return currencies.includes(currency); + }); + + return preferredCurrency ?? 'USD'; +}; + export const defaultElectrumPeer: Record = { bitcoin: [ { @@ -82,7 +100,7 @@ export const initialSettingsState: TSettings = { theme: 'dark', unit: EUnit.BTC, denomination: EDenomination.modern, - selectedCurrency: 'USD', + selectedCurrency: getDefaultCurrency(), selectedLanguage: 'english', customElectrumPeers: defaultElectrumPeer, rapidGossipSyncUrl: 'https://rgs.blocktank.to/snapshot/', diff --git a/src/utils/exchange-rate/currencies.ts b/src/utils/exchange-rate/currencies.ts new file mode 100644 index 000000000..543843320 --- /dev/null +++ b/src/utils/exchange-rate/currencies.ts @@ -0,0 +1,52 @@ +// The currencies that are supported by the exchange rate API +// https://blocktank.synonym.to/fx/rates/btc/ + +export const currencies = [ + 'ARS', + 'AUD', + 'BHD', + 'BDT', + 'BMD', + 'BRL', + 'CAD', + 'CLP', + 'CNY', + 'CZK', + 'DKK', + 'EUR', + 'GEL', + 'GBP', + 'HKD', + 'HUF', + 'XDR', + 'INR', + 'IDR', + 'ILS', + 'JPY', + 'KWD', + 'MYR', + 'MXN', + 'MMK', + 'TWD', + 'NZD', + 'NOK', + 'PKR', + 'PHP', + 'PLN', + 'RUB', + 'SAR', + 'SGD', + 'ZAR', + 'KRW', + 'LKR', + 'SEK', + 'CHF', + 'USDT', + 'THB', + 'TRY', + 'USD', + 'UAH', + 'AED', + 'VEF', + 'VND', +]; diff --git a/src/utils/i18n/index.ts b/src/utils/i18n/index.ts index b9592ad4c..3477c4045 100644 --- a/src/utils/i18n/index.ts +++ b/src/utils/i18n/index.ts @@ -1,7 +1,7 @@ import i18n from 'i18next'; import ICU from 'i18next-icu'; import { initReactI18next } from 'react-i18next'; -import * as RNLocalize from 'react-native-localize'; +import { findBestLanguageTag, getTimeZone } from 'react-native-localize'; import { __ENABLE_I18NEXT_DEBUGGER__, __JEST__ } from '../../constants/env'; import { dispatch } from '../../store/helpers'; @@ -14,9 +14,7 @@ import locales, { } from './locales'; const getDeviceLanguage = (): string => { - const lang = - RNLocalize.findBestLanguageTag(Object.keys(locales))?.languageTag ?? 'en'; - return lang; + return findBestLanguageTag(Object.keys(locales))?.languageTag ?? 'en'; }; const resources = convert(locales); @@ -41,7 +39,7 @@ i18nICU returnNull: false, }) .then(() => { - let timeZone = RNLocalize.getTimeZone(); + let timeZone = getTimeZone(); // if polyfill is used, we need to set default timezone // https://formatjs.io/docs/polyfills/intl-datetimeformat/#default-timezone diff --git a/yarn.lock b/yarn.lock index 5d3353fe7..a05fd174b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5889,7 +5889,7 @@ __metadata: react-native-image-picker: 8.1.0 react-native-keyboard-accessory: 0.1.16 react-native-keychain: 9.2.3 - react-native-localize: 3.0.2 + react-native-localize: 3.4.1 react-native-mmkv: 2.12.2 react-native-modal: 13.0.1 react-native-permissions: 5.2.5 @@ -12640,9 +12640,9 @@ __metadata: languageName: node linkType: hard -"react-native-localize@npm:3.0.2": - version: 3.0.2 - resolution: "react-native-localize@npm:3.0.2" +"react-native-localize@npm:3.4.1": + version: 3.4.1 + resolution: "react-native-localize@npm:3.4.1" peerDependencies: react: ">=18.1.0" react-native: ">=0.70.0" @@ -12650,7 +12650,7 @@ __metadata: peerDependenciesMeta: react-native-macos: optional: true - checksum: 46abd6046dfb1fb5426f9e51971894e893556646206b7fa387c5eb9394a3f3b9ddce190bc94ffc9c51c65326f60bbad57219d8330b4a1dcfaf46dd2eb4ef43ae + checksum: 2ec4c1992d88c208af3688e9be128ef16c293280677d5493bcae7cee7f66f59c73b91db2370b1879383b9ae5618caab3d569e51024b11d976a732afd5440e87d languageName: node linkType: hard