Merge pull request #2340 from BlueWallet/Contextifyfiatandlanguage

FIX: #Contextify currency and language #2173
This commit is contained in:
GLaDOS 2020-12-19 18:52:55 +00:00 committed by GitHub
commit f5e4b655cd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 231 additions and 179 deletions

View File

@ -1397,7 +1397,7 @@ export const BlueTransactionListItem = React.memo(({ item, itemPriceUnit = Bitco
const [subtitleNumberOfLines, setSubtitleNumberOfLines] = useState(1);
const { colors } = useTheme();
const { navigate } = useNavigation();
const { txMetadata, wallets } = useContext(BlueStorageContext);
const { txMetadata, wallets, preferredFiatCurrency, language } = useContext(BlueStorageContext);
const containerStyle = useMemo(
() => ({
backgroundColor: 'transparent',
@ -1415,7 +1415,8 @@ export const BlueTransactionListItem = React.memo(({ item, itemPriceUnit = Bitco
} else {
return transactionTimeToReadable(item.received);
}
}, [item.confirmations, item.received]);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [item.confirmations, item.received, language]);
const txMemo = txMetadata[item.hash]?.memo ?? '';
const subtitle = useMemo(() => {
let sub = item.confirmations < 7 ? loc.formatString(loc.transactions.list_conf, { number: item.confirmations }) : '';
@ -1446,7 +1447,8 @@ export const BlueTransactionListItem = React.memo(({ item, itemPriceUnit = Bitco
} else {
return formatBalanceWithoutSuffix(item.value && item.value, itemPriceUnit, true).toString();
}
}, [item, itemPriceUnit]);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [item, itemPriceUnit, preferredFiatCurrency]);
const rowTitleStyle = useMemo(() => {
let color = colors.successColor;

View File

@ -1,5 +1,7 @@
/* eslint-disable react/prop-types */
import { useAsyncStorage } from '@react-native-community/async-storage';
import React, { createContext, useEffect, useState } from 'react';
import { AppStorage } from '../class';
const BlueApp = require('../BlueApp');
const BlueElectrum = require('./BlueElectrum');
@ -9,6 +11,10 @@ export const BlueStorageProvider = ({ children }) => {
const [pendingWallets, setPendingWallets] = useState([]);
const [selectedWallet, setSelectedWallet] = useState('');
const [walletsInitialized, setWalletsInitialized] = useState(false);
const [preferredFiatCurrency, _setPreferredFiatCurrency] = useState();
const [language, _setLanguage] = useState();
const getPreferredCurrencyAsyncStorage = useAsyncStorage(AppStorage.PREFERRED_CURRENCY).getItem;
const getLanguageAsyncStorage = useAsyncStorage(AppStorage.LANG).getItem;
const [newWalletAdded, setNewWalletAdded] = useState(false);
const saveToDisk = async () => {
BlueApp.tx_metadata = txMetadata;
@ -21,6 +27,30 @@ export const BlueStorageProvider = ({ children }) => {
setWallets(BlueApp.getWallets());
}, []);
const getPreferredCurrency = async () => {
const item = await getPreferredCurrencyAsyncStorage();
_setPreferredFiatCurrency(item);
};
const setPreferredFiatCurrency = () => {
getPreferredCurrency();
};
const getLanguage = async () => {
const item = await getLanguageAsyncStorage();
_setLanguage(item);
};
const setLanguage = () => {
getLanguage();
};
useEffect(() => {
getPreferredCurrency();
getLanguageAsyncStorage();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const resetWallets = () => {
setWallets(BlueApp.getWallets());
};
@ -149,6 +179,10 @@ export const BlueStorageProvider = ({ children }) => {
setResetOnAppUninstallTo,
isPasswordInUse,
setIsAdancedModeEnabled,
setPreferredFiatCurrency,
preferredFiatCurrency,
setLanguage,
language,
}}
>
{children}

View File

@ -1,4 +1,4 @@
import React, { useRef, useCallback, useState, useImperativeHandle, forwardRef } from 'react';
import React, { useRef, useCallback, useState, useImperativeHandle, forwardRef, useContext } from 'react';
import PropTypes from 'prop-types';
import {
ActivityIndicator,
@ -21,6 +21,8 @@ import { LightningCustodianWallet, MultisigHDWallet, PlaceholderWallet } from '.
import WalletGradient from '../class/wallet-gradient';
import { BluePrivateBalance } from '../BlueComponents';
import { BlueStorageContext } from '../blue_modules/storage-context';
const nStyles = StyleSheet.create({
root: {
marginVertical: 17,
@ -268,6 +270,7 @@ const cStyles = StyleSheet.create({
const WalletsCarousel = forwardRef((props, ref) => {
const carouselRef = useRef();
const [loading, setLoading] = useState(true);
const { preferredFiatCurrency, language } = useContext(BlueStorageContext);
const renderItem = useCallback(
({ item, index }) => (
<WalletCarouselItem
@ -278,7 +281,8 @@ const WalletsCarousel = forwardRef((props, ref) => {
onPress={props.onPress}
/>
),
[props.vertical, props.selectedWallet, props.handleLongPress, props.onPress],
// eslint-disable-next-line react-hooks/exhaustive-deps
[props.vertical, props.selectedWallet, props.handleLongPress, props.onPress, preferredFiatCurrency, language],
);
useImperativeHandle(ref, () => ({

View File

@ -13,125 +13,131 @@ const currency = require('../blue_modules/currency');
dayjs.extend(relativeTime);
// first-time loading sequence
(async () => {
const setDateTimeLocale = async () => {
let lang = await AsyncStorage.getItem('lang');
let localeForDayJSAvailable = true;
switch (lang) {
case 'ar':
require('dayjs/locale/ar');
break;
case 'bg_bg':
lang = 'bg';
require('dayjs/locale/bg');
break;
case 'ca':
require('dayjs/locale/ca');
break;
case 'cy':
require('dayjs/locale/cy');
break;
case 'da_dk':
require('dayjs/locale/da');
break;
case 'de_de':
require('dayjs/locale/de');
break;
case 'el':
require('dayjs/locale/el');
break;
case 'es':
require('dayjs/locale/es');
break;
case 'es_419':
// es-do it is the closes one to es_419
lang = 'es-do';
require('dayjs/locale/es-do');
break;
case 'fi_fi':
require('dayjs/locale/fi');
break;
case 'fa':
require('dayjs/locale/fa');
break;
case 'fr_fr':
require('dayjs/locale/fr');
break;
case 'he':
require('dayjs/locale/he');
break;
case 'hr_hr':
require('dayjs/locale/hr');
break;
case 'hu_hu':
require('dayjs/locale/hu');
break;
case 'id_id':
require('dayjs/locale/id');
break;
case 'it':
require('dayjs/locale/it');
break;
case 'jp_jp':
lang = 'ja';
require('dayjs/locale/ja');
break;
case 'nb_no':
require('dayjs/locale/nb');
break;
case 'nl_nl':
require('dayjs/locale/nl');
break;
case 'pt_br':
lang = 'pt-br';
require('dayjs/locale/pt-br');
break;
case 'pt_pt':
lang = 'pt';
require('dayjs/locale/pt');
break;
case 'pl':
require('dayjs/locale/pl');
break;
case 'ru':
require('dayjs/locale/ru');
break;
case 'sk_sk':
require('dayjs/locale/sk');
break;
case 'sl_si':
require('dayjs/locale/sl');
break;
case 'sv_se':
require('dayjs/locale/sv');
break;
case 'th_th':
require('dayjs/locale/th');
break;
case 'tr_tr':
require('dayjs/locale/tr');
break;
case 'vi_vn':
require('dayjs/locale/vi');
break;
case 'zh_cn':
lang = 'zh-cn';
require('dayjs/locale/zh-cn');
break;
case 'zh_tw':
lang = 'zh-tw';
require('dayjs/locale/zh-tw');
break;
default:
localeForDayJSAvailable = false;
break;
}
if (localeForDayJSAvailable) {
dayjs.locale(lang.split('_')[0]);
}
};
const setLanguageLocale = async () => {
// finding out whether lang preference was saved
// For some reason using the AppStorage.LANG constant is not working. Hard coding string for now.
let lang = await AsyncStorage.getItem('lang');
const lang = await AsyncStorage.getItem('lang');
if (lang) {
strings.setLanguage(lang);
let localeForDayJSAvailable = true;
switch (lang) {
case 'ar':
require('dayjs/locale/ar');
break;
case 'bg_bg':
lang = 'bg';
require('dayjs/locale/bg');
break;
case 'ca':
require('dayjs/locale/ca');
break;
case 'cy':
require('dayjs/locale/cy');
break;
case 'da_dk':
require('dayjs/locale/da');
break;
case 'de_de':
require('dayjs/locale/de');
break;
case 'el':
require('dayjs/locale/el');
break;
case 'es':
require('dayjs/locale/es');
break;
case 'es_419':
// es-do it is the closes one to es_419
lang = 'es-do';
require('dayjs/locale/es-do');
break;
case 'fi_fi':
require('dayjs/locale/fi');
break;
case 'fa':
require('dayjs/locale/fa');
break;
case 'fr_fr':
require('dayjs/locale/fr');
break;
case 'he':
require('dayjs/locale/he');
break;
case 'hr_hr':
require('dayjs/locale/hr');
break;
case 'hu_hu':
require('dayjs/locale/hu');
break;
case 'id_id':
require('dayjs/locale/id');
break;
case 'it':
require('dayjs/locale/it');
break;
case 'jp_jp':
lang = 'ja';
require('dayjs/locale/ja');
break;
case 'nb_no':
require('dayjs/locale/nb');
break;
case 'nl_nl':
require('dayjs/locale/nl');
break;
case 'pt_br':
lang = 'pt-br';
require('dayjs/locale/pt-br');
break;
case 'pt_pt':
lang = 'pt';
require('dayjs/locale/pt');
break;
case 'pl':
require('dayjs/locale/pl');
break;
case 'ru':
require('dayjs/locale/ru');
break;
case 'sk_sk':
require('dayjs/locale/sk');
break;
case 'sl_si':
require('dayjs/locale/sl');
break;
case 'sv_se':
require('dayjs/locale/sv');
break;
case 'th_th':
require('dayjs/locale/th');
break;
case 'tr_tr':
require('dayjs/locale/tr');
break;
case 'vi_vn':
require('dayjs/locale/vi');
break;
case 'zh_cn':
lang = 'zh-cn';
require('dayjs/locale/zh-cn');
break;
case 'zh_tw':
lang = 'zh-tw';
require('dayjs/locale/zh-tw');
break;
default:
localeForDayJSAvailable = false;
break;
}
if (localeForDayJSAvailable) {
dayjs.locale(lang.split('_')[0]);
}
await setDateTimeLocale();
} else {
const locales = RNLocalize.getLocales();
if (Object.keys(AvailableLanguages).some(language => language === locales[0])) {
@ -142,7 +148,8 @@ dayjs.extend(relativeTime);
strings.setLanguage('en');
}
}
})();
};
setLanguageLocale();
const strings = new Localization({
en: require('./en.json'),
@ -184,7 +191,11 @@ const strings = new Localization({
zh_tw: require('./zh_tw.json'),
});
strings.saveLanguage = lang => AsyncStorage.setItem(AppStorage.LANG, lang);
strings.saveLanguage = async lang => {
await AsyncStorage.setItem(AppStorage.LANG, lang);
strings.setLanguage(lang);
await setDateTimeLocale();
};
export const transactionTimeToReadable = time => {
if (time === 0) {

View File

@ -1,15 +1,17 @@
import React, { useState, useEffect } from 'react';
import React, { useState, useEffect, useContext } from 'react';
import { FlatList, ActivityIndicator, View, StyleSheet } from 'react-native';
import { SafeBlueArea, BlueListItem, BlueText, BlueCard, BlueNavigationStyle } from '../../BlueComponents';
import PropTypes from 'prop-types';
import { FiatUnit, FiatUnitSource } from '../../models/fiatUnit';
import loc from '../../loc';
import { useTheme } from '@react-navigation/native';
import { BlueStorageContext } from '../../blue_modules/storage-context';
const currency = require('../../blue_modules/currency');
const data = Object.values(FiatUnit);
const Currency = () => {
const { setPreferredFiatCurrency } = useContext(BlueStorageContext);
const [isSavingNewPreferredCurrency, setIsSavingNewPreferredCurrency] = useState(false);
const [selectedCurrency, setSelectedCurrency] = useState(null);
const { colors } = useTheme();
@ -62,6 +64,7 @@ const Currency = () => {
await currency.setPrefferedCurrency(item);
await currency.startUpdater();
setIsSavingNewPreferredCurrency(false);
setPreferredFiatCurrency();
}}
/>
);

View File

@ -1,9 +1,12 @@
import React, { useState, useEffect, useCallback } from 'react';
import React, { useState, useEffect, useContext } from 'react';
import { FlatList, StyleSheet } from 'react-native';
import { SafeBlueArea, BlueListItem, BlueCard, BlueLoading, BlueNavigationStyle, BlueText } from '../../BlueComponents';
import { SafeBlueArea, BlueListItem, BlueLoading, BlueNavigationStyle } from '../../BlueComponents';
import { AvailableLanguages } from '../../loc/languages';
import loc from '../../loc';
import { BlueStorageContext } from '../../blue_modules/storage-context';
import { useNavigation, useTheme } from '@react-navigation/native';
const styles = StyleSheet.create({
flex: {
flex: 1,
@ -11,42 +14,50 @@ const styles = StyleSheet.create({
});
const Language = () => {
const { setLanguage, language } = useContext(BlueStorageContext);
const [isLoading, setIsLoading] = useState(true);
const [language, setLanguage] = useState(loc.getLanguage());
const [selectedLanguage, setSelectedLanguage] = useState(loc.getLanguage());
const { setOptions } = useNavigation();
const { colors } = useTheme();
const stylesHook = StyleSheet.create({
flex: {
backgroundColor: colors.background,
},
});
useEffect(() => {
setIsLoading(false);
}, []);
const renderItem = useCallback(
({ item }) => {
return (
<BlueListItem
onPress={() => {
console.log('setLanguage', item.value);
loc.saveLanguage(item.value);
setLanguage(item.value);
}}
title={item.label}
{...(language === item.value
? {
rightIcon: { name: 'check', type: 'octaicon', color: '#0070FF' },
}
: { hideChevron: true })}
/>
);
},
[language],
);
useEffect(() => {
setOptions({ headerTitle: loc.settings.language });
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [language]);
const renderItem = item => {
return (
<BlueListItem
onPress={async () => {
await loc.saveLanguage(item.item.value);
setSelectedLanguage(item.item.value);
setLanguage();
}}
title={item.item.label}
checkmark={selectedLanguage === item.item.value}
/>
);
};
return isLoading ? (
<BlueLoading />
) : (
<SafeBlueArea forceInset={{ horizontal: 'always' }} style={styles.flex}>
<FlatList style={styles.flex} keyExtractor={(_item, index) => `${index}`} data={AvailableLanguages} renderItem={renderItem} />
<BlueCard>
<BlueText>{loc.settings.language_restart}</BlueText>
</BlueCard>
<SafeBlueArea forceInset={{ horizontal: 'always' }} style={[styles.flex, stylesHook.flex]}>
<FlatList
style={[styles.flex, stylesHook.flex]}
keyExtractor={(_item, index) => `${index}`}
data={AvailableLanguages}
renderItem={renderItem}
initialNumToRender={25}
/>
</SafeBlueArea>
);
};

View File

@ -1,8 +1,9 @@
import React from 'react';
import { ScrollView, TouchableOpacity, StyleSheet, StatusBar } from 'react-native';
import React, { useContext } from 'react';
import { ScrollView, StyleSheet, StatusBar } from 'react-native';
import { BlueListItem, BlueNavigationStyle, BlueHeaderDefaultSubHooks } from '../../BlueComponents';
import { useNavigation } from '@react-navigation/native';
import loc from '../../loc';
import { BlueStorageContext } from '../../blue_modules/storage-context';
const styles = StyleSheet.create({
root: {
@ -12,36 +13,22 @@ const styles = StyleSheet.create({
const Settings = () => {
const { navigate } = useNavigation();
// By simply having it here, it'll re-render the UI if language is changed
// eslint-disable-next-line no-unused-vars
const { language } = useContext(BlueStorageContext);
return (
<ScrollView style={styles.root}>
<StatusBar barStyle="default" />
<BlueHeaderDefaultSubHooks leftText={loc.settings.header} rightComponent={null} />
<BlueListItem title={loc.settings.general} component={TouchableOpacity} onPress={() => navigate('GeneralSettings')} chevron />
<BlueListItem title={loc.settings.currency} component={TouchableOpacity} onPress={() => navigate('Currency')} chevron />
<BlueListItem title={loc.settings.language} component={TouchableOpacity} onPress={() => navigate('Language')} chevron />
<BlueListItem
title={loc.settings.encrypt_title}
onPress={() => navigate('EncryptStorage')}
component={TouchableOpacity}
testID="SecurityButton"
chevron
/>
<BlueListItem title={loc.settings.network} component={TouchableOpacity} onPress={() => navigate('NetworkSettings')} chevron />
<BlueListItem
title={loc.settings.notifications}
component={TouchableOpacity}
onPress={() => navigate('NotificationSettings')}
chevron
/>
<BlueListItem title={loc.settings.privacy} component={TouchableOpacity} onPress={() => navigate('SettingsPrivacy')} chevron />
<BlueListItem
title={loc.settings.about}
component={TouchableOpacity}
onPress={() => navigate('About')}
testID="AboutButton"
chevron
/>
<BlueListItem title={loc.settings.general} onPress={() => navigate('GeneralSettings')} chevron />
<BlueListItem title={loc.settings.currency} onPress={() => navigate('Currency')} chevron />
<BlueListItem title={loc.settings.language} onPress={() => navigate('Language')} chevron />
<BlueListItem title={loc.settings.encrypt_title} onPress={() => navigate('EncryptStorage')} testID="SecurityButton" chevron />
<BlueListItem title={loc.settings.network} onPress={() => navigate('NetworkSettings')} chevron />
<BlueListItem title={loc.settings.notifications} onPress={() => navigate('NotificationSettings')} chevron />
<BlueListItem title={loc.settings.privacy} onPress={() => navigate('SettingsPrivacy')} chevron />
<BlueListItem title={loc.settings.about} onPress={() => navigate('About')} testID="AboutButton" chevron />
</ScrollView>
);
};