REF: sync up with master

This commit is contained in:
Ivan Vershigora 2020-09-09 20:51:49 +03:00
commit ce777f8051
28 changed files with 976 additions and 535 deletions

3
.gitignore vendored
View file

@ -65,3 +65,6 @@ artifacts/
# Editors
.vscode/
*.mx
*.realm
*.realm.lock

4
App.js
View file

@ -15,7 +15,6 @@ import Clipboard from '@react-native-community/clipboard';
import Modal from 'react-native-modal';
import { NavigationContainer, CommonActions } from '@react-navigation/native';
import { SafeAreaProvider } from 'react-native-safe-area-context';
import Navigation from './Navigation';
import { navigationRef } from './NavigationService';
import * as NavigationService from './NavigationService';
import { BlueTextCentered, BlueButton, SecondButton } from './BlueComponents';
@ -27,6 +26,7 @@ import OnAppLaunch from './class/on-app-launch';
import DeeplinkSchemaMatch from './class/deeplink-schema-match';
import loc from './loc';
import { BlueDefaultTheme, BlueDarkTheme, BlueCurrentTheme } from './components/themes';
import InitRoot from './Navigation';
const A = require('./blue_modules/analytics');
if (process.env.NODE_ENV !== 'development') {
@ -298,7 +298,7 @@ export default class App extends React.Component {
<SafeAreaProvider>
<View style={styles.root}>
<NavigationContainer ref={navigationRef} theme={this.state.theme === 'dark' ? BlueDarkTheme : BlueDefaultTheme}>
<Navigation />
<InitRoot />
</NavigationContainer>
{this.renderClipboardContentModal()}
</View>

View file

@ -897,6 +897,49 @@ export const BlueHeaderDefaultSubHooks = props => {
);
};
export const BlueHeaderDefaultMainHooks = props => {
const { colors } = useTheme();
return (
<Header
{...props}
leftComponent={{
text: props.leftText,
style: {
fontWeight: 'bold',
fontSize: 34,
color: colors.foregroundColor,
},
}}
leftContainerStyle={{
minWidth: '70%',
height: 80,
}}
bottomDivider={false}
topDivider={false}
containerStyle={{
height: 44,
flexDirection: 'row',
backgroundColor: colors.elevatated,
borderTopColor: colors.elevatated,
borderBottomColor: colors.elevatated,
borderBottomWidth: 0,
}}
rightComponent={
props.onNewWalletPress && (
<TouchableOpacity
onPress={props.onNewWalletPress}
style={{
height: 100,
}}
>
<BluePlusIcon />
</TouchableOpacity>
)
}
/>
);
};
export class BlueHeaderDefaultMain extends Component {
render() {
return (
@ -923,6 +966,7 @@ export class BlueHeaderDefaultMain extends Component {
backgroundColor: BlueCurrentTheme.colors.background,
borderTopColor: BlueCurrentTheme.colors.background,
borderBottomColor: BlueCurrentTheme.colors.background,
borderBottomWidth: 0,
}}
rightComponent={
this.props.onNewWalletPress && (
@ -1410,7 +1454,12 @@ export class ManageFundsBigButton extends Component {
export class NewWalletPanel extends Component {
render() {
return (
<TouchableOpacity testID="CreateAWallet" {...this.props} onPress={this.props.onPress} style={{ marginVertical: 17 }}>
<TouchableOpacity
testID="CreateAWallet"
{...this.props}
onPress={this.props.onPress}
style={{ marginVertical: 17, paddingRight: 10 }}
>
<View
style={{
paddingHorizontal: 24,
@ -1658,7 +1707,7 @@ export const BlueTransactionListItem = React.memo(({ item, itemPriceUnit = Bitco
);
});
const WalletCarouselItem = ({ item, index, onPress, handleLongPress }) => {
const WalletCarouselItem = ({ item, index, onPress, handleLongPress, isSelectedWallet }) => {
const scaleValue = new Animated.Value(1.0);
const onPressedIn = () => {
@ -1758,9 +1807,14 @@ const WalletCarouselItem = ({ item, index, onPress, handleLongPress }) => {
</Animated.View>
);
} else {
let opacity = 1.0;
if (isSelectedWallet === false) {
opacity = 0.5;
}
return (
<Animated.View
style={{ paddingRight: 10, marginVertical: 17, transform: [{ scale: scaleValue }] }}
style={{ paddingRight: 10, marginVertical: 17, transform: [{ scale: scaleValue }], opacity }}
shadowOpacity={40 / 100}
shadowOffset={{ width: 0, height: 0 }}
shadowRadius={5}
@ -1863,7 +1917,15 @@ export class WalletsCarousel extends Component {
state = { isLoading: true };
_renderItem = ({ item, index }) => {
return <WalletCarouselItem item={item} index={index} handleLongPress={this.props.handleLongPress} onPress={this.props.onPress} />;
return (
<WalletCarouselItem
isSelectedWallet={this.props.vertical && this.props.selectedWallet && item ? this.props.selectedWallet === item.getID() : undefined}
item={item}
index={index}
handleLongPress={this.props.handleLongPress}
onPress={this.props.onPress}
/>
);
};
snapToItem = item => {
@ -1885,7 +1947,6 @@ export class WalletsCarousel extends Component {
</View>
)}
<Carousel
{...this.props}
ref={this.walletsCarousel}
renderItem={this._renderItem}
sliderWidth={sliderWidth}
@ -1894,9 +1955,10 @@ export class WalletsCarousel extends Component {
inactiveSlideScale={1}
inactiveSlideOpacity={0.7}
activeSlideAlignment="start"
initialNumToRender={4}
initialNumToRender={10}
onLayout={this.onLayout}
contentContainerCustomStyle={{ left: 20 }}
{...this.props}
/>
</>
);

View file

@ -1,7 +1,7 @@
// import { createAppContainer } from '@react-navigation/native';
import React from 'react';
import { createStackNavigator, TransitionPresets } from '@react-navigation/stack';
import { Platform, Dimensions } from 'react-native';
import { createDrawerNavigator } from '@react-navigation/drawer';
import { Platform, useWindowDimensions, Dimensions } from 'react-native';
import Settings from './screen/settings/settings';
import About from './screen/settings/about';
@ -66,17 +66,18 @@ import LnurlPaySuccess from './screen/lnd/lnurlPaySuccess';
import LoadingScreen from './LoadingScreen';
import UnlockWith from './UnlockWith';
import { BlueNavigationStyle } from './BlueComponents';
import DrawerList from './screen/wallets/drawerList';
import { isTablet } from 'react-native-device-info';
const SCREEN_HEIGHT = Dimensions.get('window').height;
const defaultScreenOptions =
Platform.OS === 'ios'
? ({ route, navigation }) => ({
gestureEnabled: true,
gestureResponseDistance: { vertical: SCREEN_HEIGHT, horizontal: 50 },
cardOverlayEnabled: true,
cardStyle: { backgroundColor: '#FFFFFF' },
headerStatusBarHeight: navigation.dangerouslyGetState().routes.indexOf(route) > 0 ? 10 : undefined,
...TransitionPresets.ModalPresentationIOS,
gestureResponseDistance: { vertical: Dimensions.get('window').height, horizontal: 50 },
})
: undefined;
const defaultStackScreenOptions =
@ -250,17 +251,50 @@ const HodlHodlLoginRoot = () => (
</HodlHodlLoginStack.Navigator>
);
const RootStack = createStackNavigator();
const Navigation = () => (
<RootStack.Navigator mode="modal" screenOptions={defaultScreenOptions} initialRouteName="LoadingScreenRoot">
{/* stacks */}
<RootStack.Screen name="LoadingScreenRoot" component={LoadingScreenRoot} options={{ headerShown: false, animationEnabled: false }} />
<RootStack.Screen
const Drawer = createDrawerNavigator();
function DrawerRoot() {
const dimensions = useWindowDimensions();
const isLargeScreen = Platform.OS === 'android' ? isTablet() : dimensions.width >= Dimensions.get('screen').width / 3 && isTablet();
const drawerStyle = { width: '0%' };
return (
<Drawer.Navigator
drawerStyle={isLargeScreen ? null : drawerStyle}
drawerType={isLargeScreen ? 'permanent' : null}
drawerContent={props => <DrawerList {...props} />}
>
<Drawer.Screen name="Navigation" component={Navigation} options={{ headerShown: false, gestureEnabled: false }} />
</Drawer.Navigator>
);
}
const InitStack = createStackNavigator();
const InitRoot = () => (
<InitStack.Navigator screenOptions={defaultScreenOptions} initialRouteName="LoadingScreenRoot">
<InitStack.Screen name="LoadingScreenRoot" component={LoadingScreenRoot} options={{ headerShown: false, animationEnabled: false }} />
<InitStack.Screen
name="UnlockWithScreenRoot"
component={UnlockWithScreenRoot}
options={{ headerShown: false, animationEnabled: false }}
/>
<RootStack.Screen name="WalletsRoot" component={WalletsRoot} options={{ headerShown: false, animationEnabled: false }} />
<InitStack.Screen
name="ScanQRCodeRoot"
component={ScanQRCodeRoot}
options={{
...TransitionPresets.ModalTransition,
headerShown: false,
gestureResponseDistance: { vertical: Dimensions.get('window').height, horizontal: 50 },
}}
/>
<InitStack.Screen name="ReorderWallets" component={ReorderWallets} options={ReorderWallets.navigationOptions} />
<InitStack.Screen name="DrawerRoot" component={DrawerRoot} options={{ headerShown: false, animationEnabled: false }} />
</InitStack.Navigator>
);
const RootStack = createStackNavigator();
const Navigation = () => (
<RootStack.Navigator mode="modal" screenOptions={defaultScreenOptions} initialRouteName="LoadingScreenRoot">
{/* stacks */}
<RootStack.Screen name="WalletsRoot" component={WalletsRoot} options={{ headerShown: false }} />
<RootStack.Screen name="AddWalletRoot" component={AddWalletRoot} options={{ headerShown: false, gestureEnabled: false }} />
<RootStack.Screen name="SendDetailsRoot" component={SendDetailsRoot} options={{ headerShown: false }} />
<RootStack.Screen name="LNDCreateInvoiceRoot" component={LNDCreateInvoiceRoot} options={{ headerShown: false }} />
@ -269,15 +303,7 @@ const Navigation = () => (
<RootStack.Screen name="HodlHodlLoginRoot" component={HodlHodlLoginRoot} options={{ headerShown: false }} />
<RootStack.Screen name="HodlHodlMyContracts" component={HodlHodlMyContracts} options={HodlHodlMyContracts.navigationOptions} />
<RootStack.Screen name="HodlHodlWebview" component={HodlHodlWebview} options={HodlHodlWebview.navigationOptions} />
<RootStack.Screen
name="ScanQRCodeRoot"
component={ScanQRCodeRoot}
options={{
...TransitionPresets.ModalTransition,
headerShown: false,
gestureResponseDistance: { vertical: SCREEN_HEIGHT, horizontal: 50 },
}}
/>
{/* screens */}
<RootStack.Screen name="WalletExport" component={WalletExport} options={WalletExport.navigationOptions} />
<RootStack.Screen name="WalletXpub" component={WalletXpub} options={WalletXpub.navigationOptions} />
@ -286,8 +312,7 @@ const Navigation = () => (
<RootStack.Screen name="SelectWallet" component={SelectWallet} options={{ headerLeft: null }} />
<RootStack.Screen name="ReceiveDetails" component={ReceiveDetails} options={ReceiveDetails.navigationOptions} />
<RootStack.Screen name="LappBrowser" component={LappBrowser} options={LappBrowser.navigationOptions} />
<RootStack.Screen name="ReorderWallets" component={ReorderWallets} options={ReorderWallets.navigationOptions} />
</RootStack.Navigator>
);
export default Navigation;
export default InitRoot;

View file

@ -81,7 +81,7 @@ export default class UnlockWith extends Component {
successfullyAuthenticated = () => {
EV(EV.enum.WALLETS_INITIALIZED);
NavigationService.dispatch(StackActions.replace('WalletsRoot'));
NavigationService.dispatch(StackActions.replace('DrawerRoot'));
};
unlockWithBiometrics = async () => {

View file

@ -122,7 +122,6 @@ def enableHermes = project.ext.react.get("enableHermes", false);
android {
compileSdkVersion rootProject.ext.compileSdkVersion
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8

View file

@ -51,7 +51,6 @@
android:label="@string/app_name"
android:launchMode="singleInstance"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode"
android:screenOrientation="portrait"
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

View file

@ -1,5 +1,11 @@
package io.bluewallet.bluewallet;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.os.PersistableBundle;
import androidx.annotation.Nullable;
import com.facebook.react.ReactActivity;
public class MainActivity extends ReactActivity {
@ -12,4 +18,12 @@ public class MainActivity extends ReactActivity {
protected String getMainComponentName() {
return "BlueWallet";
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getResources().getBoolean(R.bool.portrait_only)) {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
}
}

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<bool name="portrait_only">false</bool>
</resources>

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<bool name="portrait_only">false</bool>
</resources>

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<bool name="portrait_only">true</bool>
</resources>

View file

@ -250,13 +250,13 @@ PODS:
- React
- react-native-blur (0.8.0):
- React
- react-native-camera (3.35.0):
- react-native-camera (3.38.0):
- React
- react-native-camera/RCT (= 3.35.0)
- react-native-camera/RN (= 3.35.0)
- react-native-camera/RCT (3.35.0):
- react-native-camera/RCT (= 3.38.0)
- react-native-camera/RN (= 3.38.0)
- react-native-camera/RCT (3.38.0):
- React
- react-native-camera/RN (3.35.0):
- react-native-camera/RN (3.38.0):
- React
- react-native-document-picker (3.5.4):
- React
@ -359,6 +359,8 @@ PODS:
- React
- RNHandoff (0.0.3):
- React
- RNInAppBrowser (3.4.0):
- React
- RNLocalize (1.4.0):
- React
- RNQuickAction (0.3.13):
@ -367,6 +369,8 @@ PODS:
- React
- RNReactNativeHapticFeedback (1.10.0):
- React
- RNReanimated (1.13.0):
- React
- RNScreens (2.10.1):
- React
- RNSecureKeyStore (1.0.0):
@ -466,10 +470,12 @@ DEPENDENCIES:
- RNFS (from `../node_modules/react-native-fs`)
- RNGestureHandler (from `../node_modules/react-native-gesture-handler`)
- RNHandoff (from `../node_modules/react-native-handoff`)
- RNInAppBrowser (from `../node_modules/react-native-inappbrowser-reborn`)
- RNLocalize (from `../node_modules/react-native-localize`)
- RNQuickAction (from `../node_modules/react-native-quick-actions`)
- RNRate (from `../node_modules/react-native-rate`)
- RNReactNativeHapticFeedback (from `../node_modules/react-native-haptic-feedback`)
- RNReanimated (from `../node_modules/react-native-reanimated`)
- RNScreens (from `../node_modules/react-native-screens`)
- RNSecureKeyStore (from `../node_modules/react-native-secure-key-store/ios`)
- "RNSentry (from `../node_modules/@sentry/react-native`)"
@ -602,6 +608,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native-gesture-handler"
RNHandoff:
:path: "../node_modules/react-native-handoff"
RNInAppBrowser:
:path: "../node_modules/react-native-inappbrowser-reborn"
RNLocalize:
:path: "../node_modules/react-native-localize"
RNQuickAction:
@ -610,6 +618,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native-rate"
RNReactNativeHapticFeedback:
:path: "../node_modules/react-native-haptic-feedback"
RNReanimated:
:path: "../node_modules/react-native-reanimated"
RNScreens:
:path: "../node_modules/react-native-screens"
RNSecureKeyStore:
@ -663,7 +673,7 @@ SPEC CHECKSUMS:
react-native-biometrics: c892904948a32295b128f633bcc11eda020645c5
react-native-blue-crypto: 23f1558ad3d38d7a2edb7e2f6ed1bc520ed93e56
react-native-blur: cad4d93b364f91e7b7931b3fa935455487e5c33c
react-native-camera: 9dd96065b956306de03ef2a2efc3583019f95941
react-native-camera: 9a0db39fc97a479fe472e86ce424545478133a2f
react-native-document-picker: c5752781fbc0c126c627c1549b037c139444a4cf
react-native-geolocation: cbd9d6bd06bac411eed2671810f454d4908484a8
react-native-image-picker: a6c3d644751a388b0fc8b56822ff7cbd398a3008
@ -694,10 +704,12 @@ SPEC CHECKSUMS:
RNFS: 2bd9eb49dc82fa9676382f0585b992c424cd59df
RNGestureHandler: b6b359bb800ae399a9c8b27032bdbf7c18f08a08
RNHandoff: d3b0754cca3a6bcd9b25f544f733f7f033ccf5fa
RNInAppBrowser: 6097fbc6b09051b40a6a9ec22caf7af40b115ec0
RNLocalize: fc27ee5878ce5a3af73873fb2d8e866e0d1e6d84
RNQuickAction: 6d404a869dc872cde841ad3147416a670d13fa93
RNRate: 2b31dad120cd1b78e33c6034808561c386a3dddf
RNReactNativeHapticFeedback: 22c5ecf474428766c6b148f96f2ff6155cd7225e
RNReanimated: 89f5e0a04d1dd52fbf27e7e7030d8f80a646a3fc
RNScreens: b748efec66e095134c7166ca333b628cd7e6f3e2
RNSecureKeyStore: f1ad870e53806453039f650720d2845c678d89c8
RNSentry: 2bae4ffee2e66376ef42cb845a494c3bde17bc56

View file

@ -50,4 +50,4 @@ Lightning Network
Прямо в вашем кошельке - это настоящая финансовая революция.
Local Trader
Покупайте и продавайте биткоины напрямую с другим пользователям, без третьих лиц.
Покупайте и продавайте биткоины напрямую с другим пользователям, без третьих лиц.

View file

@ -7,4 +7,4 @@
* Повышение комиссии за транзакцию (RBF)
* SegWit
* Просмотр баланса кошелька без импорта приватных ключей
* Lightning network
* Lightning network

View file

@ -299,6 +299,7 @@
"add_entropy_provide": "Entropia por meio de jogadas de dados",
"add_entropy_remain": "{gen} bytes de entropia gerada. Os bytes {rem} restantes serão obtidos do gerador de números aleatórios do sistema.",
"add_import_wallet": "Importar carteira",
"import_file": "Importar arquivo",
"add_lightning": "Lightning",
"add_lndhub": "Conectar ao seu LNDHub",
"add_lndhub_error": "O endereço fornecido não é um LNDHub válido",
@ -344,7 +345,7 @@
"list_empty_txs1_lightning": "A carteira Relâmpago faz transações super rápidas (coisa de segundos) e tem taxas ridiculamente baratas, ideal para transações diárias e de baixo valor.",
"list_empty_txs2": "nenhuma no momento",
"list_empty_txs2_lightning": "\nPara começar a usar clique em \"administrar fundos\" e recarregue o seu saldo.",
"list_header": "Uma carteira representa um par de um segredo (chave privada) e um endereço que você pode compartilhar para receber moedas.",
"list_header": "Uma carteira representa um par de chaves, uma chave privada e uma que voce pode compartilhar para receber moedas.",
"list_import_error": "Foi encontrado um erro ao tentar importar esta carteira.",
"list_import_problem": "Ocorreu um problema ao importar esta carteira",
"list_latest_transaction": "última transação",

View file

@ -299,6 +299,7 @@
"add_entropy_provide": "Entropia através de dados",
"add_entropy_remain": "{gen} bytes de entropia gerada. Os bytes {rem} restantes serão obtidos do gerador de números aleatórios do sistema.",
"add_import_wallet": "Importar wallet",
"import_file": "Importar ficheiro",
"add_lightning": "Lightning",
"add_lndhub": "Conecte-se ao seu LNDHub",
"add_lndhub_error": "O endereço de nó fornecido não é um nó LNDHub válido.",
@ -344,7 +345,7 @@
"list_empty_txs1_lightning": "A wallet Lightning deve ser usada para as suas transações diárias. As taxas são muito baixas e a velocidade muito elevada",
"list_empty_txs2": "nenhuma de momento",
"list_empty_txs2_lightning": "\nPara começar a usar toque em \"gerir fundos\" e recarregue o seu saldo.",
"list_header": "Uma carteira representa um par de um segredo (chave privada) e um endereço que você pode compartilhar para receber moedas.",
"list_header": "Uma carteira representa um par de chaves, uma privada e outra que pode partilhar para receber bitcoin.",
"list_import_error": "Foi encontrado um erro ao tentar importar esta carteira.",
"list_import_problem": "Ocorreu um problema ao importar esta carteira",
"list_latest_transaction": "últimas transacções",

View file

@ -15,9 +15,11 @@ export const FiatUnit = Object.freeze({
ILS: { endPointKey: 'ILS', symbol: '₪', locale: 'he-IL' },
INR: { endPointKey: 'INR', symbol: '₹', locale: 'hi-HN' },
JPY: { endPointKey: 'JPY', symbol: '¥', locale: 'ja-JP' },
KES: { endPointKey: 'KES', symbol: 'Ksh', locale: 'en-KE' },
KRW: { endPointKey: 'KRW', symbol: '₩', locale: 'ko-KR' },
MXN: { endPointKey: 'MXN', symbol: '$', locale: 'es-MX' },
MYR: { endPointKey: 'MYR', symbol: 'RM', locale: 'ms-MY' },
NGN: { endPointKey: 'NGN', symbol: '₦', locale: 'en-NG' },
NOK: { endPointKey: 'NOK', symbol: 'kr', locale: 'nb-NO' },
NZD: { endPointKey: 'NZD', symbol: '$', locale: 'en-NZ' },
PLN: { endPointKey: 'PLN', symbol: 'zł', locale: 'pl-PL' },
@ -25,6 +27,7 @@ export const FiatUnit = Object.freeze({
SGD: { endPointKey: 'SGD', symbol: 'S$', locale: 'zh-SG' },
SEK: { endPointKey: 'SEK', symbol: 'kr', locale: 'sv-SE' },
THB: { endPointKey: 'THB', symbol: '฿', locale: 'th-TH' },
TWD: { endPointKey: 'TWD', symbol: '$', locale: 'zh-Hant-TW' },
UAH: { endPointKey: 'UAH', symbol: '₴', locale: 'uk-UA' },
VEF: { endPointKey: 'VEF', symbol: 'Bs.', locale: 'es-VE' },
ZAR: { endPointKey: 'ZAR', symbol: 'R', locale: 'en-ZA' },

49
package-lock.json generated
View file

@ -4655,6 +4655,15 @@
}
}
},
"@react-navigation/drawer": {
"version": "5.9.0",
"resolved": "https://registry.npmjs.org/@react-navigation/drawer/-/drawer-5.9.0.tgz",
"integrity": "sha512-YcuJ9QD4cFyjfXJx6vMsG3u3bfOU/Nt+GvMMl+4rZOxw2LStmubY1jqfsEAli/+dTUHv5kXJf5dF+/GhUCqA5g==",
"requires": {
"color": "^3.1.2",
"react-native-iphone-x-helper": "^1.2.1"
}
},
"@react-navigation/native": {
"version": "5.7.3",
"resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-5.7.3.tgz",
@ -7318,9 +7327,9 @@
}
},
"dayjs": {
"version": "1.8.32",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.8.32.tgz",
"integrity": "sha512-V91aTRu5btP+uzGHaaOfodckEfBWhmi9foRP7cauAO1PTB8+tZ9o0Jec7q6TIIRY1N4q1IfiKsZunkB/AEWqMQ=="
"version": "1.8.33",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.8.33.tgz",
"integrity": "sha512-881TDLZCdpJFKbraWRHcUG8zfMLLX400ENf9rFZDuWc5zYMss6xifo2PhlDX0ftOmR2NRmaIY47bAa4gKQfXqw=="
},
"debug": {
"version": "4.1.1",
@ -7792,9 +7801,9 @@
}
},
"elliptic": {
"version": "6.5.2",
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.2.tgz",
"integrity": "sha512-f4x70okzZbIQl/NSRLkI/+tteV/9WqL98zx+SQ69KbXxmVrmjwsNUPn/gYJJ0sHvEak24cZgHIPegRePAtA/xw==",
"version": "6.5.3",
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz",
"integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==",
"requires": {
"bn.js": "^4.4.0",
"brorand": "^1.0.1",
@ -12148,9 +12157,9 @@
}
},
"lodash": {
"version": "4.17.15",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A=="
"version": "4.17.20",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA=="
},
"lodash.isequal": {
"version": "4.5.0",
@ -14971,9 +14980,9 @@
"from": "git+https://github.com/Overtorment/react-native-blue-crypto.git"
},
"react-native-camera": {
"version": "3.35.0",
"resolved": "https://registry.npmjs.org/react-native-camera/-/react-native-camera-3.35.0.tgz",
"integrity": "sha512-hs9ja6qc2JMB2oYDzYuzOF1GUMRZY0O7KX1e7K7WJBQzAwsXnlwm4d2r6P61kGMk1FUDUn186Yc3kb5vfEAyoA==",
"version": "3.38.0",
"resolved": "https://registry.npmjs.org/react-native-camera/-/react-native-camera-3.38.0.tgz",
"integrity": "sha512-mczUap9TWHeFuGOY5fCoMnPWv704BiPyGNU5p4filB/XuQL4/M93Teqgw6gWMdd8KfElY9uqH4QNtHruJ67A5A==",
"requires": {
"prop-types": "^15.6.2"
}
@ -15049,6 +15058,14 @@
"resolved": "https://registry.npmjs.org/react-native-image-picker/-/react-native-image-picker-2.3.3.tgz",
"integrity": "sha512-i/7JDnxKkUGSbFY2i7YqFNn3ncJm1YlcrPKXrXmJ/YUElz8tHkuwknuqBd9QCJivMfHX41cmq4XvdBDwIOtO+A=="
},
"react-native-inappbrowser-reborn": {
"version": "git+https://github.com/BlueWallet/react-native-inappbrowser.git#54ab29283b105b70496feb2e1edb4e002c4d9b3e",
"from": "git+https://github.com/BlueWallet/react-native-inappbrowser.git#v3.4.0",
"requires": {
"invariant": "^2.2.4",
"opencollective-postinstall": "^2.0.2"
}
},
"react-native-iphone-x-helper": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/react-native-iphone-x-helper/-/react-native-iphone-x-helper-1.2.1.tgz",
@ -15171,6 +15188,14 @@
"prop-types": "^15.7.2"
}
},
"react-native-reanimated": {
"version": "1.13.0",
"resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-1.13.0.tgz",
"integrity": "sha512-uadP/0QO+4TCsyPSvzRdl+76NPM7Bp8M25KQLB4Hg3tWBMjhrMrETnzNi33L/OPfmhU+7rceyi0QPe/DxKT5bQ==",
"requires": {
"fbjs": "^1.0.0"
}
},
"react-native-safe-area-context": {
"version": "3.1.4",
"resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-3.1.4.tgz",

View file

@ -68,6 +68,7 @@
"@react-native-community/masked-view": "0.1.10",
"@react-native-community/push-notification-ios": "1.4.0",
"@react-native-community/slider": "3.0.3",
"@react-navigation/drawer": "5.9.0",
"@react-navigation/native": "5.7.3",
"@react-navigation/stack": "5.9.0",
"@remobile/react-native-qrcode-local-image": "git+https://github.com/BlueWallet/react-native-qrcode-local-image.git",
@ -87,7 +88,7 @@
"buffer-reverse": "1.0.1",
"coinselect": "3.1.12",
"crypto-js": "3.1.9-1",
"dayjs": "1.8.32",
"dayjs": "1.8.33",
"detox": "16.9.2",
"ecurve": "1.0.6",
"electrum-client": "git+https://github.com/BlueWallet/rn-electrum-client.git#99c75385f99e82b87fbfed2abfb2cccd322fe3e9",
@ -112,7 +113,7 @@
"react-native": "0.62.2",
"react-native-biometrics": "git+https://github.com/BlueWallet/react-native-biometrics.git#f62b0b193e10376d4a0e8195c700a364ed4e5aaa",
"react-native-blue-crypto": "git+https://github.com/Overtorment/react-native-blue-crypto.git",
"react-native-camera": "3.35.0",
"react-native-camera": "3.38.0",
"react-native-default-preference": "1.4.3",
"react-native-device-info": "6.0.1",
"react-native-document-picker": "git+https://github.com/BlueWallet/react-native-document-picker.git#3684d4fcc2bc0b47c32be39024e4796004c3e428",
@ -122,6 +123,7 @@
"react-native-handoff": "git+https://github.com/marcosrdz/react-native-handoff.git",
"react-native-haptic-feedback": "1.10.0",
"react-native-image-picker": "2.3.3",
"react-native-inappbrowser-reborn": "git+https://github.com/BlueWallet/react-native-inappbrowser.git#v3.4.0",
"react-native-level-fs": "3.0.1",
"react-native-linear-gradient": "2.5.6",
"react-native-localize": " 1.4.0",
@ -136,6 +138,7 @@
"react-native-quick-actions": "0.3.13",
"react-native-randombytes": "3.5.3",
"react-native-rate": "1.2.4",
"react-native-reanimated": "1.13.0",
"react-native-safe-area-context": "3.1.4",
"react-native-screens": "2.10.1",
"react-native-secure-key-store": "git+https://github.com/BlueWallet/react-native-secure-key-store.git#4ba25dedb3d5ae15c22fd0ea0555116055630966",

View file

@ -40,7 +40,7 @@ const styles = StyleSheet.create({
marginHorizontal: 56,
marginVertical: 16,
minHeight: 45,
alignContent: 'center',
alignItems: 'center',
},
scanRoot: {
height: 36,
@ -205,7 +205,6 @@ export default class LNDCreateInvoice extends Component {
async createInvoice() {
this.setState({ isLoading: true }, async () => {
try {
this.setState({ isLoading: false });
let amount = this.state.amount;
switch (this.state.unit) {
case BitcoinUnit.SATS:

View file

@ -54,6 +54,9 @@ const styles = StyleSheet.create({
alignItems: 'center',
marginVertical: 4,
},
payButtonContainer: {
alignItems: 'center',
},
walletWrapTouch: {
flexDirection: 'row',
alignItems: 'center',
@ -435,7 +438,9 @@ export default class ScanLndInvoice extends React.Component {
<ActivityIndicator />
</View>
) : (
<BlueButton title={loc.lnd.payButton} onPress={() => this.pay()} disabled={this.shouldDisablePayButton()} />
<View style={styles.payButtonContainer}>
<BlueButton title={loc.lnd.payButton} onPress={() => this.pay()} disabled={this.shouldDisablePayButton()} />
</View>
)}
</BlueCard>
</BlueCard>

View file

@ -53,7 +53,7 @@ export default class Confirm extends Component {
broadcast() {
this.setState({ isLoading: true }, async () => {
try {
await BlueElectrum.ping();
// await BlueElectrum.ping();
await BlueElectrum.waitTillConnected();
if (this.isBiometricUseCapableAndEnabled) {
@ -72,7 +72,7 @@ export default class Confirm extends Component {
EV(EV.enum.REMOTE_TRANSACTIONS_COUNT_CHANGED); // someone should fetch txs
let amount = 0;
const recipients = this.state.recipients;
if (recipients[0].amount === BitcoinUnit.MAX || !recipients[0].amount) {
if (recipients[0].amount === BitcoinUnit.MAX || (!recipients[0].amount && !recipients[0].value)) {
amount = this.state.fromWallet.getBalance() - this.state.feeSatoshi;
} else {
for (const recipient of recipients) {

View file

@ -46,7 +46,6 @@ import { BlueCurrentTheme } from '../../components/themes';
const bitcoin = require('bitcoinjs-lib');
const currency = require('../../blue_modules/currency');
const BigNumber = require('bignumber.js');
const { width } = Dimensions.get('window');
const BlueApp: AppStorage = require('../../BlueApp');
const btcAddressRx = /^[a-zA-Z0-9]{26,35}$/;
@ -140,7 +139,7 @@ const styles = StyleSheet.create({
createButton: {
marginHorizontal: 56,
marginVertical: 16,
alignContent: 'center',
alignItems: 'center',
minHeight: 44,
},
select: {
@ -262,6 +261,7 @@ export default class SendDetails extends Component {
feeSliderValue: 1,
amountUnit: fromWallet.preferredBalanceUnit, // default for whole screen
renderWalletSelectionButtonHidden: false,
width: Dimensions.get('window').width - 320,
};
}
}
@ -473,7 +473,7 @@ export default class SendDetails extends Component {
} else if (index === this.state.addresses.length - 1) {
this.scrollView.scrollToEnd();
} else {
const page = Math.round(width * (this.state.addresses.length - 2));
const page = Math.round(this.state.width * (this.state.addresses.length - 2));
this.scrollView.scrollTo({ x: page, y: 0, animated: true });
}
this.setState({ isLoading: false, recipientsScrollIndex: index });
@ -619,9 +619,10 @@ export default class SendDetails extends Component {
renderFeeSelectionModal = () => {
return (
<Modal
deviceHeight={Dimensions.get('window').height}
deviceWidth={this.state.width + this.state.width / 2}
isVisible={this.state.isFeeSelectionModalVisible}
style={styles.bottomModal}
deviceHeight={Dimensions.get('window').height}
onBackdropPress={() => {
if (this.state.fee < 1 || this.state.feeSliderValue < 1) {
this.setState({ fee: Number(1), feeSliderValue: Number(1) });
@ -726,8 +727,9 @@ export default class SendDetails extends Component {
const isSendMaxUsed = this.state.addresses.some(element => element.amount === BitcoinUnit.MAX);
return (
<Modal
isVisible={this.state.isAdvancedTransactionOptionsVisible}
deviceHeight={Dimensions.get('window').height}
deviceWidth={this.state.width + this.state.width / 2}
isVisible={this.state.isAdvancedTransactionOptionsVisible}
style={styles.bottomModal}
onBackdropPress={() => {
Keyboard.dismiss();
@ -864,7 +866,7 @@ export default class SendDetails extends Component {
Keyboard.dismiss();
var offset = e.nativeEvent.contentOffset;
if (offset) {
const page = Math.round(offset.x / width);
const page = Math.round(offset.x / this.state.width);
if (this.state.recipientsScrollIndex !== page) {
this.setState({ recipientsScrollIndex: page });
}
@ -875,7 +877,7 @@ export default class SendDetails extends Component {
Keyboard.dismiss();
var offset = this.scrollView.contentOffset;
if (offset) {
const page = Math.round(offset.x / width);
const page = Math.round(offset.x / this.state.width);
return page;
}
return 0;
@ -883,9 +885,10 @@ export default class SendDetails extends Component {
renderBitcoinTransactionInfoFields = () => {
const rows = [];
for (const [index, item] of this.state.addresses.entries()) {
rows.push(
<View key={index} style={{ minWidth: width, maxWidth: width, width: width }}>
<View key={index} style={{ width: this.state.width }}>
<BlueBitcoinAmount
isLoading={this.state.isLoading}
amount={item.amount ? item.amount.toString() : null}
@ -979,7 +982,7 @@ export default class SendDetails extends Component {
this.setState({
addresses: [recipient],
units: [BitcoinUnit.BTC],
recipientsScrollIndex: 0,
recipientsScrollIndex: 320,
isAdvancedTransactionOptionsVisible: false,
});
},
@ -991,6 +994,10 @@ export default class SendDetails extends Component {
);
};
onLayout = e => {
this.setState({ width: e.nativeEvent.layout.width });
};
render() {
if (this.state.isLoading || typeof this.state.fromWallet === 'undefined') {
return (
@ -1001,7 +1008,7 @@ export default class SendDetails extends Component {
}
return (
<TouchableWithoutFeedback onPress={Keyboard.dismiss} accessible={false}>
<View style={styles.root}>
<View style={styles.root} onLayout={this.onLayout}>
<StatusBar barStyle="light-content" />
<View>
<KeyboardAvoidingView behavior="position">

View file

@ -1,13 +1,13 @@
import React, { Component } from 'react';
import { StyleSheet, StatusBar, Linking } from 'react-native';
import { StyleSheet, StatusBar, Linking, Platform } from 'react-native';
import { BlueNavigationStyle, BlueLoading, SafeBlueArea } from '../../BlueComponents';
import PropTypes from 'prop-types';
import { WebView } from 'react-native-webview';
import { getSystemName } from 'react-native-device-info';
import { AppStorage, LightningCustodianWallet, WatchOnlyWallet } from '../../class';
import InAppBrowser from 'react-native-inappbrowser-reborn';
import * as NavigationService from '../../NavigationService';
const currency = require('../../blue_modules/currency');
const BlueApp: AppStorage = require('../../BlueApp');
const styles = StyleSheet.create({
root: {
flex: 1,
@ -23,19 +23,15 @@ export default class BuyBitcoin extends Component {
this.state = {
isLoading: true,
wallet,
address: '',
uri: '',
};
}
async componentDidMount() {
console.log('buyBitcoin - componentDidMount');
static async generateURL(wallet) {
let preferredCurrency = await currency.getPreferredCurrency();
preferredCurrency = preferredCurrency.endPointKey;
/** @type {AbstractHDWallet|WatchOnlyWallet|LightningCustodianWallet} */
const wallet = this.state.wallet;
let address = '';
@ -60,23 +56,24 @@ export default class BuyBitcoin extends Component {
}
}
const { safelloStateToken } = this.props.route.params;
let uri = 'https://bluewallet.io/buy-bitcoin-redirect.html?address=' + address;
if (safelloStateToken) {
uri += '&safelloStateToken=' + safelloStateToken;
}
if (preferredCurrency) {
uri += '&currency=' + preferredCurrency;
}
return uri;
}
if (getSystemName() === 'Mac OS X') {
Linking.openURL(uri).finally(() => this.props.navigation.goBack(null));
} else {
this.setState({ uri, isLoading: false, address });
async componentDidMount() {
console.log('buyBitcoin - componentDidMount');
let uri = await BuyBitcoin.generateURL(this.state.wallet);
const { safelloStateToken } = this.props.route.params;
if (safelloStateToken) {
uri += '&safelloStateToken=' + safelloStateToken;
}
this.setState({ uri, isLoading: false });
}
render() {
@ -105,9 +102,6 @@ BuyBitcoin.propTypes = {
safelloStateToken: PropTypes.string,
}),
}),
navigation: PropTypes.shape({
goBack: PropTypes.func,
}),
};
BuyBitcoin.navigationOptions = ({ navigation }) => ({
@ -115,3 +109,19 @@ BuyBitcoin.navigationOptions = ({ navigation }) => ({
title: '',
headerLeft: null,
});
BuyBitcoin.navigate = async wallet => {
const uri = await BuyBitcoin.generateURL(wallet);
if (Platform.OS === 'ios') {
InAppBrowser.isAvailable()
.then(_value => InAppBrowser.open(uri, { dismissButtonStyle: 'done' }))
.catch(error => {
console.log(error);
Linking.openURL(uri);
});
} else {
NavigationService.navigate('BuyBitcoin', {
wallet,
});
}
};

View file

@ -0,0 +1,312 @@
import React, { useRef, useState, useEffect } from 'react';
import { StatusBar, View, TouchableOpacity, InteractionManager, StyleSheet, Alert, useWindowDimensions } from 'react-native';
import { DrawerContentScrollView } from '@react-navigation/drawer';
import { WalletsCarousel, BlueNavigationStyle, BlueHeaderDefaultMainHooks } from '../../BlueComponents';
import { Icon } from 'react-native-elements';
import ReactNativeHapticFeedback from 'react-native-haptic-feedback';
import PropTypes from 'prop-types';
import { AppStorage, PlaceholderWallet } from '../../class';
import WalletImport from '../../class/wallet-import';
import * as NavigationService from '../../NavigationService';
import loc from '../../loc';
import { BlueCurrentTheme } from '../../components/themes';
import { useTheme, useRoute } from '@react-navigation/native';
import { SafeAreaView } from 'react-native-safe-area-context';
const EV = require('../../blue_modules/events');
const BlueApp: AppStorage = require('../../BlueApp');
const BlueElectrum = require('../../blue_modules/BlueElectrum');
const DrawerList = props => {
const walletsCarousel = useRef();
const [wallets, setWallets] = useState(BlueApp.getWallets().concat(false));
const height = useWindowDimensions().height;
const { colors } = useTheme();
const { selectedWallet } = useRoute().params || '';
const stylesHook = StyleSheet.create({
root: {
backgroundColor: colors.brandingColor,
},
});
let lastSnappedTo = 0;
const refreshTransactions = () => {
InteractionManager.runAfterInteractions(async () => {
let noErr = true;
try {
// await BlueElectrum.ping();
await BlueElectrum.waitTillConnected();
const balanceStart = +new Date();
await BlueApp.fetchWalletBalances(lastSnappedTo || 0);
const balanceEnd = +new Date();
console.log('fetch balance took', (balanceEnd - balanceStart) / 1000, 'sec');
const start = +new Date();
await BlueApp.fetchWalletTransactions(lastSnappedTo || 0);
const end = +new Date();
console.log('fetch tx took', (end - start) / 1000, 'sec');
} catch (err) {
noErr = false;
console.warn(err);
}
if (noErr) await BlueApp.saveToDisk(); // caching
redrawScreen();
});
};
useEffect(() => {
EV(EV.enum.TRANSACTIONS_COUNT_CHANGED);
console.log('drawerList wallets changed');
}, [wallets]);
const redrawScreen = (scrollToEnd = false) => {
console.log('drawerList redrawScreen()');
const newWallets = BlueApp.getWallets().concat(false);
if (scrollToEnd) {
scrollToEnd = newWallets.length > wallets.length;
}
setWallets(newWallets);
if (scrollToEnd) {
// eslint-disable-next-line no-unused-expressions
walletsCarousel.current?.snapToItem(wallets.length - 2);
}
};
useEffect(() => {
// here, when we receive REMOTE_TRANSACTIONS_COUNT_CHANGED we fetch TXs and balance for current wallet.
// placing event subscription here so it gets exclusively re-subscribed more often. otherwise we would
// have to unsubscribe on unmount and resubscribe again on mount.
EV(EV.enum.REMOTE_TRANSACTIONS_COUNT_CHANGED, refreshTransactions, true);
EV(EV.enum.WALLETS_COUNT_CHANGED, () => redrawScreen(true));
console.log('drawerList useEffect');
// the idea is that upon wallet launch we will refresh
// all balances and all transactions here:
redrawScreen();
InteractionManager.runAfterInteractions(async () => {
try {
await BlueElectrum.waitTillConnected();
const balanceStart = +new Date();
await BlueApp.fetchWalletBalances();
const balanceEnd = +new Date();
console.log('fetch all wallet balances took', (balanceEnd - balanceStart) / 1000, 'sec');
const start = +new Date();
await BlueApp.fetchWalletTransactions();
const end = +new Date();
console.log('fetch all wallet txs took', (end - start) / 1000, 'sec');
redrawScreen();
await BlueApp.saveToDisk();
} catch (error) {
console.log(error);
}
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const handleClick = index => {
console.log('click', index);
const wallet = BlueApp.wallets[index];
if (wallet) {
if (wallet.type === PlaceholderWallet.type) {
Alert.alert(
loc.wallets.add_details,
loc.wallets.list_import_problem,
[
{
text: loc.wallets.details_delete,
onPress: () => {
WalletImport.removePlaceholderWallet();
EV(EV.enum.WALLETS_COUNT_CHANGED);
},
style: 'destructive',
},
{
text: loc.wallets.list_tryagain,
onPress: () => {
props.navigation.navigate('AddWalletRoot', { screen: 'ImportWallet', params: { label: wallet.getSecret() } });
WalletImport.removePlaceholderWallet();
EV(EV.enum.WALLETS_COUNT_CHANGED);
},
style: 'default',
},
],
{ cancelable: false },
);
} else {
props.navigation.navigate('WalletTransactions', {
wallet: wallet,
key: `WalletTransactions-${wallet.getID()}`,
});
}
} else {
// if its out of index - this must be last card with incentive to create wallet
if (!BlueApp.getWallets().some(wallet => wallet.type === PlaceholderWallet.type)) {
props.navigation.navigate('Navigation', { screen: 'AddWalletRoot' });
}
}
};
const handleLongPress = () => {
if (BlueApp.getWallets().length > 1 && !BlueApp.getWallets().some(wallet => wallet.type === PlaceholderWallet.type)) {
props.navigation.navigate('ReorderWallets');
} else {
ReactNativeHapticFeedback.trigger('notificationError', { ignoreAndroidSystemSettings: false });
}
};
const onSnapToItem = index => {
console.log('onSnapToItem', index);
lastSnappedTo = index;
if (index < BlueApp.getWallets().length) {
// not the last
}
if (wallets[index].type === PlaceholderWallet.type) {
return;
}
// now, lets try to fetch balance and txs for this wallet in case it has changed
lazyRefreshWallet(index);
};
/**
* Decides whether wallet with such index shoud be refreshed,
* refreshes if yes and redraws the screen
* @param index {Integer} Index of the wallet.
* @return {Promise.<void>}
*/
const lazyRefreshWallet = async index => {
/** @type {Array.<AbstractWallet>} wallets */
const wallets = BlueApp.getWallets();
if (!wallets[index]) {
return;
}
const oldBalance = wallets[index].getBalance();
let noErr = true;
let didRefresh = false;
try {
if (wallets[index] && wallets[index].type !== PlaceholderWallet.type && wallets[index].timeToRefreshBalance()) {
console.log('snapped to, and now its time to refresh wallet #', index);
await wallets[index].fetchBalance();
if (oldBalance !== wallets[index].getBalance() || wallets[index].getUnconfirmedBalance() !== 0) {
console.log('balance changed, thus txs too');
// balance changed, thus txs too
await wallets[index].fetchTransactions();
redrawScreen();
didRefresh = true;
} else if (wallets[index].timeToRefreshTransaction()) {
console.log(wallets[index].getLabel(), 'thinks its time to refresh TXs');
await wallets[index].fetchTransactions();
if (wallets[index].fetchPendingTransactions) {
await wallets[index].fetchPendingTransactions();
}
if (wallets[index].fetchUserInvoices) {
await wallets[index].fetchUserInvoices();
await wallets[index].fetchBalance(); // chances are, paid ln invoice was processed during `fetchUserInvoices()` call and altered user's balance, so its worth fetching balance again
}
redrawScreen();
didRefresh = true;
} else {
console.log('balance not changed');
}
}
} catch (Err) {
noErr = false;
console.warn(Err);
}
if (noErr && didRefresh) {
await BlueApp.saveToDisk(); // caching
}
};
const renderWalletsCarousel = () => {
return (
<WalletsCarousel
removeClippedSubviews={false}
data={wallets}
onPress={handleClick}
handleLongPress={handleLongPress}
onSnapToItem={onSnapToItem}
ref={walletsCarousel}
testID="WalletsList"
vertical
itemHeight={190}
sliderHeight={height}
contentContainerCustomStyle={styles.contentContainerCustomStyle}
inactiveSlideOpacity={1.0}
selectedWallet={selectedWallet}
/>
);
};
return (
<DrawerContentScrollView {...props} scrollEnabled={false}>
<View styles={[styles.root, stylesHook.root]}>
<StatusBar barStyle="default" />
<SafeAreaView style={styles.root}>
<BlueHeaderDefaultMainHooks
leftText={loc.wallets.list_title}
onNewWalletPress={
!BlueApp.getWallets().some(wallet => wallet.type === PlaceholderWallet.type)
? () => props.navigation.navigate('AddWalletRoot')
: null
}
/>
</SafeAreaView>
{renderWalletsCarousel()}
</View>
</DrawerContentScrollView>
);
};
export default DrawerList;
const styles = StyleSheet.create({
contentContainerCustomStyle: {
paddingRight: 10,
paddingLeft: 20,
},
root: {
flex: 1,
},
headerTouch: {
height: 48,
paddingRight: 16,
paddingLeft: 32,
paddingVertical: 10,
},
});
DrawerList.propTypes = {
navigation: PropTypes.shape({
navigate: PropTypes.func,
addListener: PropTypes.func,
}),
route: PropTypes.shape({
name: PropTypes.string,
params: PropTypes.object,
}),
};
DrawerList.navigationOptions = ({ navigation }) => {
return {
...BlueNavigationStyle(navigation, true),
title: '',
headerStyle: {
backgroundColor: BlueCurrentTheme.colors.customHeader,
borderBottomWidth: 0,
elevation: 0,
shadowOpacity: 0,
shadowOffset: { height: 0, width: 0 },
},
headerRight: () => (
<TouchableOpacity testID="SettingsButton" style={styles.headerTouch} onPress={() => NavigationService.navigate('Settings')}>
<Icon size={22} name="kebab-horizontal" type="octicon" color={BlueCurrentTheme.colors.foregroundColor} />
</TouchableOpacity>
),
};
};

View file

@ -7,11 +7,11 @@ import {
Text,
StyleSheet,
InteractionManager,
Clipboard,
SectionList,
Alert,
Platform,
Image,
Dimensions,
} from 'react-native';
import { WalletsCarousel, BlueHeaderDefaultMain, BlueTransactionListItem, BlueNavigationStyle } from '../../BlueComponents';
import { Icon } from 'react-native-elements';
@ -22,11 +22,12 @@ import { AppStorage, PlaceholderWallet } from '../../class';
import WalletImport from '../../class/wallet-import';
import ActionSheet from '../ActionSheet';
import ImagePicker from 'react-native-image-picker';
import Clipboard from '@react-native-community/clipboard';
import * as NavigationService from '../../NavigationService';
import loc from '../../loc';
import { BlueCurrentTheme } from '../../components/themes';
import { FContainer, FButton } from '../../components/FloatButtons';
import { getSystemName } from 'react-native-device-info';
import { getSystemName, isTablet } from 'react-native-device-info';
import ScanQRCode from '../send/ScanQRCode';
const EV = require('../../blue_modules/events');
const A = require('../../blue_modules/analytics');
@ -43,12 +44,16 @@ export default class WalletsList extends Component {
constructor(props) {
super(props);
const width = Dimensions.get('window').width;
this.state = {
isLoading: true,
isFlatListRefreshControlHidden: true,
wallets: BlueApp.getWallets().concat(false),
timeElpased: 0,
dataSource: [],
itemWidth: width * 0.82 > 375 ? 375 : width * 0.82,
isLargeScreen:
Platform.OS === 'android' ? isTablet() : Dimensions.get('window').width >= Dimensions.get('screen').width / 3 && isTablet(),
};
EV(EV.enum.WALLETS_COUNT_CHANGED, () => this.redrawScreen(true));
@ -63,28 +68,9 @@ export default class WalletsList extends Component {
// all balances and all transactions here:
this.redrawScreen();
InteractionManager.runAfterInteractions(async () => {
try {
await BlueElectrum.waitTillConnected();
const balanceStart = +new Date();
await BlueApp.fetchWalletBalances();
const balanceEnd = +new Date();
console.log('fetch all wallet balances took', (balanceEnd - balanceStart) / 1000, 'sec');
const start = +new Date();
await BlueApp.fetchWalletTransactions();
const end = +new Date();
console.log('fetch all wallet txs took', (end - start) / 1000, 'sec');
await BlueApp.saveToDisk();
} catch (error) {
console.log(error);
}
});
this.interval = setInterval(() => {
this.setState(prev => ({ timeElapsed: prev.timeElapsed + 1 }));
}, 60000);
this.redrawScreen();
this._unsubscribe = this.props.navigation.addListener('focus', this.onNavigationEventFocus);
}
@ -131,11 +117,6 @@ export default class WalletsList extends Component {
redrawScreen = (scrollToEnd = false) => {
console.log('wallets/list redrawScreen()');
// here, when we receive REMOTE_TRANSACTIONS_COUNT_CHANGED we fetch TXs and balance for current wallet.
// placing event subscription here so it gets exclusively re-subscribed more often. otherwise we would
// have to unsubscribe on unmount and resubscribe again on mount.
EV(EV.enum.REMOTE_TRANSACTIONS_COUNT_CHANGED, this.refreshTransactions.bind(this), true);
if (BlueApp.getBalance() !== 0) {
A(A.ENUM.GOT_NONZERO_BALANCE);
} else {
@ -157,7 +138,8 @@ export default class WalletsList extends Component {
},
() => {
if (scrollToEnd) {
this.walletsCarousel.current.snapToItem(this.state.wallets.length - 2);
// eslint-disable-next-line no-unused-expressions
this.walletsCarousel.current?.snapToItem(this.state.wallets.length - 2);
}
},
);
@ -346,6 +328,8 @@ export default class WalletsList extends Component {
onSnapToItem={this.onSnapToItem}
ref={this.walletsCarousel}
testID="WalletsList"
sliderWidth={Dimensions.get('window').width}
itemWidth={this.state.itemWidth}
/>
);
};
@ -353,7 +337,7 @@ export default class WalletsList extends Component {
renderSectionItem = item => {
switch (item.section.key) {
case WalletsListSections.CAROUSEL:
return this.renderWalletsCarousel();
return this.state.isLargeScreen ? null : this.renderWalletsCarousel();
case WalletsListSections.LOCALTRADER:
return this.renderLocalTrader();
case WalletsListSections.TRANSACTIONS:
@ -366,7 +350,7 @@ export default class WalletsList extends Component {
renderSectionHeader = ({ section }) => {
switch (section.key) {
case WalletsListSections.CAROUSEL:
return (
return this.state.isLargeScreen ? null : (
<BlueHeaderDefaultMain
leftText={loc.wallets.list_title}
onNewWalletPress={
@ -563,9 +547,17 @@ export default class WalletsList extends Component {
}
};
onLayout = _e => {
const width = Dimensions.get('window').width;
this.setState({
isLargeScreen: Platform.OS === 'android' ? isTablet() : width >= Dimensions.get('screen').width / 3 && isTablet(),
itemWidth: width * 0.82 > 375 ? 375 : width * 0.82,
});
};
render() {
return (
<View style={styles.root}>
<View style={styles.root} onLayout={this.onLayout}>
<StatusBar barStyle="default" />
<View style={styles.walletsListWrapper}>
<SectionList

File diff suppressed because it is too large Load diff

View file

@ -31,7 +31,7 @@ const WalletXpub = () => {
const { goBack } = useNavigation();
const { colors } = useTheme();
const { width, height } = useWindowDimensions();
const stylesHook = { ...styles, root: { ...styles.root, backgroundColor: colors.elevated } };
const stylesHook = StyleSheet.create({ root: { backgroundColor: colors.elevated } });
useFocusEffect(
useCallback(() => {
@ -65,11 +65,11 @@ const WalletXpub = () => {
);
return isLoading ? (
<View style={stylesHook.root}>
<View style={[styles.root, stylesHook.root]}>
<ActivityIndicator />
</View>
) : (
<SafeBlueArea style={stylesHook.root}>
<SafeBlueArea style={[styles.root, stylesHook.root]}>
<StatusBar barStyle="light-content" />
<View style={styles.container}>
<View>