Merge branch 'master' into translations_ios-fastlane-metadata-en-us-keywords-txt--master_de_DE

This commit is contained in:
Ivan 2020-09-14 13:32:26 +03:00 committed by GitHub
commit 34900b15ba
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
99 changed files with 2953 additions and 1136 deletions

62
.github/workflows/codeql-analysis.yml vendored Normal file
View file

@ -0,0 +1,62 @@
name: "CodeQL"
on:
push:
branches: [master]
pull_request:
# The branches below must be a subset of the branches above
branches: [master]
schedule:
- cron: '0 1 * * 5'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
# Override automatic language detection by changing the below list
# Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python']
language: ['javascript']
# Learn more...
# https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection
steps:
- name: Checkout repository
uses: actions/checkout@v2
with:
# We must fetch at least the immediate parents so that if this is
# a pull request then we can checkout the head.
fetch-depth: 2
# If this run was triggered by a pull request event, then checkout
# the head of the pull request instead of the merge commit.
- run: git checkout HEAD^2
if: ${{ github.event_name == 'pull_request' }}
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1

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

@ -46,6 +46,7 @@ import { BlueCurrentTheme } from './components/themes';
import loc, { formatBalance, formatBalanceWithoutSuffix, formatBalancePlain, removeTrailingZeros, transactionTimeToReadable } from './loc';
import AsyncStorage from '@react-native-community/async-storage';
import Lnurl from './class/lnurl';
import ScanQRCode from './screen/send/ScanQRCode';
/** @type {AppStorage} */
const BlueApp = require('./BlueApp');
const { height, width } = Dimensions.get('window');
@ -897,6 +898,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 +967,7 @@ export class BlueHeaderDefaultMain extends Component {
backgroundColor: BlueCurrentTheme.colors.background,
borderTopColor: BlueCurrentTheme.colors.background,
borderBottomColor: BlueCurrentTheme.colors.background,
borderBottomWidth: 0,
}}
rightComponent={
this.props.onNewWalletPress && (
@ -1160,13 +1205,11 @@ const stylesBlueIcon = StyleSheet.create({
width: 30,
height: 30,
borderRadius: 15,
backgroundColor: BlueCurrentTheme.colors.buttonBackgroundColor,
},
ballIncoming: {
width: 30,
height: 30,
borderRadius: 15,
backgroundColor: BlueCurrentTheme.colors.ballReceive,
transform: [{ rotate: '-45deg' }],
justifyContent: 'center',
},
@ -1174,20 +1217,17 @@ const stylesBlueIcon = StyleSheet.create({
width: 30,
height: 30,
borderRadius: 15,
backgroundColor: BlueCurrentTheme.colors.ballReceive,
},
ballReceive: {
width: 30,
height: 30,
borderBottomLeftRadius: 15,
backgroundColor: BlueCurrentTheme.colors.ballReceive,
transform: [{ rotate: '-45deg' }],
},
ballOutgoing: {
width: 30,
height: 30,
borderRadius: 15,
backgroundColor: BlueCurrentTheme.colors.ballOutgoing,
transform: [{ rotate: '225deg' }],
justifyContent: 'center',
},
@ -1195,13 +1235,11 @@ const stylesBlueIcon = StyleSheet.create({
width: 30,
height: 30,
borderRadius: 15,
backgroundColor: BlueCurrentTheme.colors.ballOutgoing,
},
ballOutgoingExpired: {
width: 30,
height: 30,
borderRadius: 15,
backgroundColor: BlueCurrentTheme.colors.ballOutgoingExpired,
justifyContent: 'center',
},
ballTransparrent: {
@ -1217,18 +1255,24 @@ const stylesBlueIcon = StyleSheet.create({
backgroundColor: 'gray',
},
});
export class BluePlusIcon extends Component {
render() {
export const BluePlusIcon = props => {
const { colors } = useTheme();
const stylesBlueIconHooks = StyleSheet.create({
ball: {
backgroundColor: colors.buttonBackgroundColor,
},
});
return (
<View {...this.props} style={stylesBlueIcon.container}>
<View {...props} style={stylesBlueIcon.container}>
<View style={stylesBlueIcon.box1}>
<View style={stylesBlueIcon.ball}>
<View style={[stylesBlueIcon.ball, stylesBlueIconHooks.ball]}>
<Ionicons
{...this.props}
{...props}
name="ios-add"
size={26}
style={{
color: BlueCurrentTheme.colors.foregroundColor,
color: colors.foregroundColor,
backgroundColor: 'transparent',
left: 8,
top: 1,
@ -1238,147 +1282,176 @@ export class BluePlusIcon extends Component {
</View>
</View>
);
}
}
};
export class BlueTransactionIncomingIcon extends Component {
render() {
export const BlueTransactionIncomingIcon = props => {
const { colors } = useTheme();
const stylesBlueIconHooks = StyleSheet.create({
ballIncoming: {
backgroundColor: colors.ballReceive,
},
});
return (
<View {...this.props}>
<View {...props}>
<View style={stylesBlueIcon.boxIncoming}>
<View style={stylesBlueIcon.ballIncoming}>
<Icon {...this.props} name="arrow-down" size={16} type="font-awesome" color={BlueCurrentTheme.colors.incomingForegroundColor} />
<View style={[stylesBlueIcon.ballIncoming, stylesBlueIconHooks.ballIncoming]}>
<Icon {...props} name="arrow-down" size={16} type="font-awesome" color={colors.incomingForegroundColor} />
</View>
</View>
</View>
);
}
}
};
export class BlueTransactionPendingIcon extends Component {
render() {
export const BlueTransactionPendingIcon = props => {
const { colors } = useTheme();
const stylesBlueIconHooks = StyleSheet.create({
ball: {
backgroundColor: colors.buttonBackgroundColor,
},
});
return (
<View {...this.props}>
<View {...props}>
<View style={stylesBlueIcon.boxIncoming}>
<View style={stylesBlueIcon.ball}>
<View style={[stylesBlueIcon.ball, stylesBlueIconHooks.ball]}>
<Icon
{...this.props}
{...props}
name="kebab-horizontal"
size={16}
type="octicon"
color={BlueCurrentTheme.colors.foregroundColor}
color={colors.foregroundColor}
iconStyle={{ left: 0, top: 7 }}
/>
</View>
</View>
</View>
);
}
}
};
export class BlueTransactionExpiredIcon extends Component {
render() {
export const BlueTransactionExpiredIcon = props => {
const { colors } = useTheme();
const stylesBlueIconHooks = StyleSheet.create({
ballOutgoingExpired: {
backgroundColor: colors.ballOutgoingExpired,
},
});
return (
<View {...this.props}>
<View {...props}>
<View style={stylesBlueIcon.boxIncoming}>
<View style={stylesBlueIcon.ballOutgoingExpired}>
<Icon {...this.props} name="clock" size={16} type="octicon" color="#9AA0AA" iconStyle={{ left: 0, top: 0 }} />
<View style={[stylesBlueIcon.ballOutgoingExpired, stylesBlueIconHooks.ballOutgoingExpired]}>
<Icon {...props} name="clock" size={16} type="octicon" color="#9AA0AA" iconStyle={{ left: 0, top: 0 }} />
</View>
</View>
</View>
);
}
}
};
export class BlueTransactionOnchainIcon extends Component {
render() {
export const BlueTransactionOnchainIcon = props => {
const { colors } = useTheme();
const stylesBlueIconHooks = StyleSheet.create({
ballIncoming: {
backgroundColor: colors.ballReceive,
},
});
return (
<View {...this.props}>
<View {...props}>
<View style={stylesBlueIcon.boxIncoming}>
<View style={stylesBlueIcon.ballIncoming}>
<View style={[stylesBlueIcon.ballIncoming, stylesBlueIconHooks.ballIncoming]}>
<Icon
{...this.props}
{...props}
name="link"
size={16}
type="font-awesome"
color={BlueCurrentTheme.colors.incomingForegroundColor}
color={colors.incomingForegroundColor}
iconStyle={{ left: 0, top: 0, transform: [{ rotate: '-45deg' }] }}
/>
</View>
</View>
</View>
);
}
}
};
export class BlueTransactionOffchainIcon extends Component {
render() {
export const BlueTransactionOffchainIcon = props => {
const { colors } = useTheme();
const stylesBlueIconHooks = StyleSheet.create({
ballOutgoingWithoutRotate: {
backgroundColor: colors.ballOutgoing,
},
});
return (
<View {...this.props}>
<View {...props}>
<View style={stylesBlueIcon.boxIncoming}>
<View style={stylesBlueIcon.ballOutgoingWithoutRotate}>
<View style={[stylesBlueIcon.ballOutgoingWithoutRotate, stylesBlueIconHooks.ballOutgoingWithoutRotate]}>
<Icon
{...this.props}
{...props}
name="bolt"
size={16}
type="font-awesome"
color={BlueCurrentTheme.colors.outgoingForegroundColor}
color={colors.outgoingForegroundColor}
iconStyle={{ left: 0, marginTop: 6 }}
/>
</View>
</View>
</View>
);
}
}
};
export class BlueTransactionOffchainIncomingIcon extends Component {
render() {
export const BlueTransactionOffchainIncomingIcon = props => {
const { colors } = useTheme();
const stylesBlueIconHooks = StyleSheet.create({
ballIncomingWithoutRotate: {
backgroundColor: colors.ballReceive,
},
});
return (
<View {...this.props}>
<View {...props}>
<View style={stylesBlueIcon.boxIncoming}>
<View style={stylesBlueIcon.ballIncomingWithoutRotate}>
<View style={[stylesBlueIcon.ballIncomingWithoutRotate, stylesBlueIconHooks.ballIncomingWithoutRotate]}>
<Icon
{...this.props}
{...props}
name="bolt"
size={16}
type="font-awesome"
color={BlueCurrentTheme.colors.incomingForegroundColor}
color={colors.incomingForegroundColor}
iconStyle={{ left: 0, marginTop: 6 }}
/>
</View>
</View>
</View>
);
}
}
};
export class BlueTransactionOutgoingIcon extends Component {
render() {
export const BlueTransactionOutgoingIcon = props => {
const { colors } = useTheme();
const stylesBlueIconHooks = StyleSheet.create({
ballOutgoing: {
backgroundColor: colors.ballOutgoing,
},
});
return (
<View {...this.props}>
<View {...props}>
<View style={stylesBlueIcon.boxIncoming}>
<View style={stylesBlueIcon.ballOutgoing}>
<Icon {...this.props} name="arrow-down" size={16} type="font-awesome" color={BlueCurrentTheme.colors.outgoingForegroundColor} />
<View style={[stylesBlueIcon.ballOutgoing, stylesBlueIconHooks.ballOutgoing]}>
<Icon {...props} name="arrow-down" size={16} type="font-awesome" color={colors.outgoingForegroundColor} />
</View>
</View>
</View>
);
}
}
};
const sendReceiveScanButtonFontSize =
PixelRatio.roundToNearestPixel(Dimensions.get('window').width / 26) > 22
? 22
: PixelRatio.roundToNearestPixel(Dimensions.get('window').width / 26);
export class BlueReceiveButtonIcon extends Component {
render() {
export const BlueReceiveButtonIcon = props => {
const { colors } = useTheme();
return (
<TouchableOpacity {...this.props} style={{ flex: 1 }}>
<TouchableOpacity {...props} style={{ flex: 1 }}>
<View
style={{
flex: 1,
backgroundColor: BlueCurrentTheme.colors.buttonBackgroundColor,
backgroundColor: colors.buttonBackgroundColor,
}}
>
<View style={{ flex: 1, flexDirection: 'row', alignItems: 'center', justifyContent: 'center' }}>
@ -1392,16 +1465,16 @@ export class BlueReceiveButtonIcon extends Component {
}}
>
<Icon
{...this.props}
{...props}
name="arrow-down"
size={sendReceiveScanButtonFontSize}
type="font-awesome"
color={BlueCurrentTheme.colors.buttonAlternativeTextColor}
color={colors.buttonAlternativeTextColor}
/>
</View>
<Text
style={{
color: BlueCurrentTheme.colors.buttonAlternativeTextColor,
color: colors.buttonAlternativeTextColor,
fontWeight: '500',
fontSize: sendReceiveScanButtonFontSize,
left: 5,
@ -1414,8 +1487,7 @@ export class BlueReceiveButtonIcon extends Component {
</View>
</TouchableOpacity>
);
}
}
};
export class BlueScanButton extends Component {
render() {
@ -1544,10 +1616,10 @@ export class ManageFundsBigButton extends Component {
}
}
export class NewWalletPanel extends Component {
render() {
export const NewWalletPanel = props => {
const { colors } = useTheme();
return (
<TouchableOpacity testID="CreateAWallet" {...this.props} onPress={this.props.onPress} style={{ marginVertical: 17 }}>
<TouchableOpacity testID="CreateAWallet" {...props} onPress={props.onPress} style={{ marginVertical: 17, paddingRight: 10 }}>
<View
style={{
paddingHorizontal: 24,
@ -1563,7 +1635,7 @@ export class NewWalletPanel extends Component {
style={{
fontWeight: '600',
fontSize: 24,
color: BlueCurrentTheme.colors.foregroundColor,
color: colors.foregroundColor,
marginBottom: 4,
}}
>
@ -1572,7 +1644,7 @@ export class NewWalletPanel extends Component {
<Text
style={{
fontSize: 13,
color: BlueCurrentTheme.colors.alternativeTextColor,
color: colors.alternativeTextColor,
}}
>
{loc.wallets.list_create_a_wallet1}
@ -1581,22 +1653,22 @@ export class NewWalletPanel extends Component {
style={{
backgroundColor: 'transparent',
fontSize: 13,
color: BlueCurrentTheme.colors.alternativeTextColor,
color: colors.alternativeTextColor,
}}
>
{loc.wallets.list_create_a_wallet2}
</Text>
<View style={{ marginTop: 12, backgroundColor: '#007AFF', paddingHorizontal: 32, paddingVertical: 12, borderRadius: 8 }}>
<Text style={{ color: BlueCurrentTheme.colors.brandingColor, fontWeight: '500' }}>{loc.wallets.list_create_a_button}</Text>
<Text style={{ color: colors.brandingColor, fontWeight: '500' }}>{loc.wallets.list_create_a_button}</Text>
</View>
</View>
</TouchableOpacity>
);
}
}
};
export const BlueTransactionListItem = React.memo(({ item, itemPriceUnit = BitcoinUnit.BTC, timeElapsed }) => {
const [subtitleNumberOfLines, setSubtitleNumberOfLines] = useState(1);
const { colors } = useTheme();
const txMemo = () => {
if (BlueApp.tx_metadata[item.hash] && BlueApp.tx_metadata[item.hash].memo) {
@ -1629,7 +1701,7 @@ export const BlueTransactionListItem = React.memo(({ item, itemPriceUnit = Bitco
};
const rowTitleStyle = () => {
let color = BlueCurrentTheme.colors.successColor;
let color = colors.successColor;
if (item.type === 'user_invoice' || item.type === 'payment_request') {
const currentDate = new Date();
@ -1637,16 +1709,16 @@ export const BlueTransactionListItem = React.memo(({ item, itemPriceUnit = Bitco
const invoiceExpiration = item.timestamp + item.expire_time;
if (invoiceExpiration > now) {
color = BlueCurrentTheme.colors.successColor;
color = colors.successColor;
} else if (invoiceExpiration < now) {
if (item.ispaid) {
color = BlueCurrentTheme.colors.successColor;
color = colors.successColor;
} else {
color = '#9AA0AA';
}
}
} else if (item.value / 100000000 < 0) {
color = BlueCurrentTheme.colors.foregroundColor;
color = colors.foregroundColor;
}
return {
@ -1795,7 +1867,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 = () => {
@ -1895,9 +1967,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}
@ -2000,7 +2077,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 => {
@ -2022,7 +2107,6 @@ export class WalletsCarousel extends Component {
</View>
)}
<Carousel
{...this.props}
ref={this.walletsCarousel}
renderItem={this._renderItem}
sliderWidth={sliderWidth}
@ -2031,9 +2115,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}
/>
</>
);
@ -2095,6 +2180,8 @@ export class BlueAddressInput extends Component {
alert(loc.send.qr_error_no_qrcode);
}
});
} else if (response.error) {
ScanQRCode.presentCameraNotAuthorizedAlert(response.error);
}
},
);
@ -2105,7 +2192,7 @@ export class BlueAddressInput extends Component {
};
showActionSheet = async () => {
const isClipboardEmpty = (await Clipboard.getString()).replace(' ', '').length === 0;
const isClipboardEmpty = (await Clipboard.getString()).trim().length === 0;
let copyFromClipboardIndex;
if (Platform.OS === 'ios') {
const options = [loc._.cancel, loc.wallets.list_long_choose, isDesktop ? loc.wallets.take_photo : loc.wallets.list_long_scan];

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

@ -1,12 +1,3 @@
export default class WatchConnectivity {
isAppInstalled = false;
static shared = new WatchConnectivity();
wallets;
fetchTransactionsFunction = () => {};
getIsWatchAppInstalled() {}
async handleLightningInvoiceCreateRequest(_walletIndex, _amount, _description) {}
async sendWalletsToWatch() {}
}
const WatchConnectivity = () => {};
WatchConnectivity.sendWalletsToWatch = () => {};
export default WatchConnectivity;

View file

@ -1,67 +1,58 @@
import * as Watch from 'react-native-watch-connectivity';
import { updateApplicationContext, watchEvents, getIsWatchAppInstalled } from 'react-native-watch-connectivity';
import { InteractionManager } from 'react-native';
import { Chain } from './models/bitcoinUnits';
import loc, { formatBalance, transactionTimeToReadable } from './loc';
const notifications = require('./blue_modules/notifications');
export default class WatchConnectivity {
isAppInstalled = false;
static shared = new WatchConnectivity();
wallets;
fetchTransactionsFunction = () => {};
constructor() {
this.getIsWatchAppInstalled();
}
getIsWatchAppInstalled() {
Watch.getIsWatchAppInstalled((err, isAppInstalled) => {
if (!err) {
WatchConnectivity.shared.isAppInstalled = isAppInstalled;
Watch.subscribeToWatchState((err, watchState) => {
if (!err) {
if (watchState === 'Activated') {
WatchConnectivity.shared.sendWalletsToWatch();
}
}
});
Watch.subscribeToMessages(async (message, reply) => {
const WatchConnectivity = () => {
const handleMessages = (message, reply) => {
const BlueApp = require('./BlueApp');
if (message.request === 'createInvoice') {
const createInvoiceRequest = await this.handleLightningInvoiceCreateRequest(
message.walletIndex,
message.amount,
message.description,
);
reply({ invoicePaymentRequest: createInvoiceRequest });
handleLightningInvoiceCreateRequest(message.walletIndex, message.amount, message.description)
.then(createInvoiceRequest => reply({ invoicePaymentRequest: createInvoiceRequest }))
.catch(e => console.log(e));
} else if (message.message === 'sendApplicationContext') {
await WatchConnectivity.shared.sendWalletsToWatch();
WatchConnectivity.sendWalletsToWatch();
} else if (message.message === 'fetchTransactions') {
await WatchConnectivity.shared.fetchTransactionsFunction();
}
});
}
});
BlueApp.fetchWalletTransactions().then(() => BlueApp.saveToDisk());
}
};
async handleLightningInvoiceCreateRequest(walletIndex, amount, description) {
const wallet = WatchConnectivity.shared.wallets[walletIndex];
if (wallet.allowReceive() && amount > 0 && description.trim().length > 0) {
const handleLightningInvoiceCreateRequest = async (walletIndex, amount, description = loc.lnd.placeholder) => {
const BlueApp = require('./BlueApp');
const wallet = BlueApp.getWallets()[walletIndex];
if (wallet.allowReceive() && amount > 0) {
try {
const invoiceRequest = await wallet.addInvoice(amount, description);
// lets decode payreq and subscribe groundcontrol so we can receive push notification when our invoice is paid
try {
// Let's verify if notifications are already configured. Otherwise the watch app will freeze waiting for user approval in iOS app
if (await notifications.isNotificationsEnabled()) {
const decoded = await wallet.decodeInvoice(invoiceRequest);
await notifications.tryToObtainPermissions();
notifications.majorTomToGroundControl([], [decoded.payment_hash], []);
}
} catch (e) {
console.log('WatchConnectivity - Running in Simulator');
console.log(e);
}
return invoiceRequest;
} catch (error) {
return error;
}
}
}
};
async sendWalletsToWatch() {
const allWallets = WatchConnectivity.shared.wallets;
getIsWatchAppInstalled().then(installed => {
if (installed) {
watchEvents.addListener('message', handleMessages);
}
});
};
WatchConnectivity.sendWalletsToWatch = () => {
const BlueApp = require('./BlueApp');
const allWallets = BlueApp.getWallets();
if (!Array.isArray(allWallets)) {
console.log('No Wallets set to sync with Watch app. Exiting...');
return;
@ -71,7 +62,7 @@ export default class WatchConnectivity {
}
return InteractionManager.runAfterInteractions(async () => {
if (WatchConnectivity.shared.isAppInstalled) {
const BlueApp = require('./BlueApp');
const wallets = [];
for (const wallet of allWallets) {
@ -144,8 +135,8 @@ export default class WatchConnectivity {
} else {
amount = formatBalance(transaction.value, wallet.getPreferredBalanceUnit(), true).toString();
}
if (WatchConnectivity.shared.tx_metadata[transaction.hash] && WatchConnectivity.shared.tx_metadata[transaction.hash].memo) {
memo = WatchConnectivity.shared.tx_metadata[transaction.hash].memo;
if (BlueApp.tx_metadata[transaction.hash] && BlueApp.tx_metadata[transaction.hash].memo) {
memo = BlueApp.tx_metadata[transaction.hash].memo;
} else if (transaction.memo) {
memo = transaction.memo;
}
@ -162,9 +153,9 @@ export default class WatchConnectivity {
xpub: wallet.getXpub() ? wallet.getXpub() : wallet.getSecret(),
});
}
Watch.updateApplicationContext({ wallets, randomID: Math.floor(Math.random() * 11) });
updateApplicationContext({ wallets, randomID: Math.floor(Math.random() * 11) });
return { wallets };
}
});
}
}
};
export default WatchConnectivity;
WatchConnectivity();

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
@ -133,7 +132,7 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 1
versionName "5.5.7"
versionName "5.5.8"
multiDexEnabled true
missingDimensionStrategy 'react-native-camera', 'general'
testBuildType System.getProperty('testBuildType', 'debug') // This will later be used to control the test apk build type

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

@ -15,9 +15,10 @@ import {
HDLegacyElectrumSeedP2PKHWallet,
HDSegwitElectrumSeedP2WPKHWallet,
} from './';
import WatchConnectivity from '../WatchConnectivity';
import DeviceQuickActions from './quick-actions';
import { AbstractHDElectrumWallet } from './wallets/abstract-hd-electrum-wallet';
import { Platform } from 'react-native';
import WatchConnectivity from '../WatchConnectivity';
const encryption = require('../blue_modules/encryption');
const Realm = require('realm');
const createHash = require('create-hash');
@ -77,6 +78,7 @@ export class AppStorage {
}
async setResetOnAppUninstallTo(value) {
if (Platform.OS === 'ios') {
await this.setItem(AppStorage.DELETE_WALLET_AFTER_UNINSTALL, value ? '1' : '');
try {
RNSecureKeyStore.setResetOnAppUninstallTo(value);
@ -84,6 +86,7 @@ export class AppStorage {
console.warn(Error);
}
}
}
async storageIsEncrypted() {
let data;
@ -332,13 +335,7 @@ export class AppStorage {
}
}
realm.close();
WatchConnectivity.shared.wallets = this.wallets;
WatchConnectivity.shared.tx_metadata = this.tx_metadata;
WatchConnectivity.shared.fetchTransactionsFunction = async () => {
await this.fetchWalletTransactions();
await this.saveToDisk();
};
await WatchConnectivity.shared.sendWalletsToWatch();
await WatchConnectivity.sendWalletsToWatch();
const isStorageEncrypted = await this.storageIsEncrypted();
if (isStorageEncrypted) {
@ -405,14 +402,12 @@ export class AppStorage {
offloadWalletToRealm(realm, wallet) {
const id = wallet.getID();
console.log('offloading wallet id', id);
const walletToSave = wallet._hdWalletInstance ?? wallet;
if (walletToSave instanceof AbstractHDElectrumWallet) {
realm.write(() => {
const j1 = JSON.stringify(walletToSave._txs_by_external_index);
const j2 = JSON.stringify(walletToSave._txs_by_internal_index);
console.log('j1 = ', j1.length / 1024, 'kb; j2 = ', j2.length / 1024, 'kb');
realm.create(
'Wallet',
{
@ -494,9 +489,7 @@ export class AppStorage {
} else {
await this.setItem(AppStorage.FLAG_ENCRYPTED, ''); // drop the flag
}
WatchConnectivity.shared.wallets = this.wallets;
WatchConnectivity.shared.tx_metadata = this.tx_metadata;
WatchConnectivity.shared.sendWalletsToWatch();
WatchConnectivity.sendWalletsToWatch();
DeviceQuickActions.setWallets(this.wallets);
DeviceQuickActions.setQuickActions();
try {

View file

@ -226,7 +226,11 @@ export class HodlHodlApi {
validationErrorsToReadable(errorz) {
const ret = [];
for (const er of Object.keys(errorz)) {
if (Array.isArray(errorz[er])) {
ret.push(errorz[er].join('; '));
} else {
ret.push(errorz[er]);
}
}
return ret.join('\n');

View file

@ -404,10 +404,10 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
// its faster to pre-build hashmap of owned addresses than to query `this.weOwnAddress()`, which in turn
// iterates over all addresses in hierarchy
const ownedAddressesHashmap = {};
for (let c = 0; c < this.next_free_address_index + this.gap_limit; c++) {
for (let c = 0; c < this.next_free_address_index + 1; c++) {
ownedAddressesHashmap[this._getExternalAddressByIndex(c)] = true;
}
for (let c = 0; c < this.next_free_change_address_index + this.gap_limit; c++) {
for (let c = 0; c < this.next_free_change_address_index + 1; c++) {
ownedAddressesHashmap[this._getInternalAddressByIndex(c)] = true;
}
// hack: in case this code is called from LegacyWallet:

View file

@ -48,7 +48,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>5.5.7</string>
<string>5.5.8</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>
@ -72,6 +72,11 @@
<false/>
<key>LSApplicationCategoryType</key>
<string>public.app-category.finance</string>
<key>LSApplicationQueriesSchemes</key>
<array>
<string>bankid</string>
<string>swish</string>
</array>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>LSSupportsOpeningDocumentsInPlace</key>
@ -103,6 +108,8 @@
<string>This alert should not show up as we do not require this data</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Your authorization is required in order to provide you local offers.</string>
<key>NSLocationUsageDescription</key>
<string>Your authorization is required in order to provide you local offers.</string>
<key>NSMicrophoneUsageDescription</key>
<string>This alert should not show up as we do not require this data</string>
<key>NSMotionUsageDescription</key>

View file

@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>XPC!</string>
<key>CFBundleShortVersionString</key>
<string>5.5.7</string>
<string>5.5.8</string>
<key>CFBundleVersion</key>
<string>239</string>
<key>CLKComplicationPrincipalClass</key>

View file

@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>5.5.7</string>
<string>5.5.8</string>
<key>CFBundleVersion</key>
<string>239</string>
<key>UISupportedInterfaceOrientations</key>

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):
@ -380,7 +384,7 @@ PODS:
- React
- RNVectorIcons (6.6.0):
- React
- RNWatch (0.5.0):
- RNWatch (1.0.2):
- React
- Sentry (5.2.2):
- Sentry/Core (= 5.2.2)
@ -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,17 +704,19 @@ 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
RNShare: a1d5064df7a0ebe778d001869b3f0a124bf0a491
RNSVG: ce9d996113475209013317e48b05c21ee988d42e
RNVectorIcons: 0bb4def82230be1333ddaeee9fcba45f0b288ed4
RNWatch: 4de37878bbf071847e438b089c01d4ad8d4eddae
RNWatch: d56d00be49131ee454bb5a4a574f18506c8949e4
Sentry: 8fa58a051237554f22507fb483b9a1de0171a2dc
ToolTipMenu: 4d89d95ddffd7539230bdbe02ee51bbde362e37e
Yoga: 3ebccbdd559724312790e7742142d062476b698e

View file

@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>5.5.7</string>
<string>5.5.8</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>NSExtension</key>

View file

@ -0,0 +1,52 @@
محفظة Bitcoin تتيح لك تخزين عملات Bitcoin وإرسالها واستلامها وشراءها، مع التركيز على الأمان والبساطة.
ستمتلك مفاتيحك الخاصة في BlueWallet، وهي محفظة لعملات Bitcoin. محفظة Bitcoin أنشأها مستخدمو Bitcoin من أجل المجتمع.
يمكنك إجراء المعاملات مع أي شخص في العالم على الفور، وتغيير النظام المالي من جيبك مباشرةً.
يمكنك إنشاء عدد غير محدود من محافظ Bitcoin مجانًا أو استيراد محفظتك الحالية. الأمر بسيط وسريع.
_____
إليك ما تحصل عليه:
1 - الأمان من خلال التصميم
المصدر المفتوح
يمكنك إنشاؤها وتشغيلها بنفسك لأنها مرخَّصة من معهد ماساتشوستس للتكنولوجيا (MIT)! وهي مصمَّمة باستخدام ReactNative
الإنكار المقبول
كلمة مرور تفك تشفير محافظ Bitcoin المزيفة إذا اضطررت إلى الكشف عن وصولك
التشفير الكامل
علاوةً على التشفير متعدد الطبقات لنظام iOS، فإننا نقوم بتشفير كل شيء بكلمات مرور مضافة
العقدة الكاملة
اتصل بمحفظة Bitcoin باستخدام العقدة الكاملة عن طريق برنامج Electrum
التخزين البارد
اتصل بمحفظة جهاز واحتفظ بعملاتك في وحدة "التخزين البارد"
2 - التركيز على تجربتك
السيطرة على مجريات الأمور
لا تغادر المفاتيح الخاصة جهازك أبدًا.أنت من يتحكم في مفاتيحك الخاصة
مرونة الرسوم
بدايةً من ساتوشي واحد. تحددها أنت؛ أي المستخدم بنفسك
الاستبدال بالرسوم
(RBF) عزز سرعة معاملاتك بزيادة الرسوم (BIP125)
محافظ للتحقق من الرصيد فقط
تتيح لك محافظ التحقق من الرصيد متابعة وحدة التخزين البارد دون لمس الجهاز.
شبكة Lightning
محفظة Lightning دون الحاجة إلى إعدادها. معاملات رخيصة جدًا وسريعة مع توفير أفضل تجربة لمستخدمي Bitcoin.
شراء Bitcoin
ادخل في أغوار الثورة المالية المفتوحة مع القدرة على شراء Bitcoin مباشرة من محفظتك.
متداول محلي
منصة تداول Bitcoin من نظير إلى نظير، والتي تتيح لك شراء عملات Bitcoin وبيعها مباشرةً إلى المستخدمين الآخرين دون الاستعانة بأطراف خارجية.

View file

@ -0,0 +1 @@
bitcoin,محفظة,محفظة bitcoin,سلسلة الكتل,btc,عملة مشفرة,شراء bitcoin,samourai,electrum,ethereum

View file

@ -0,0 +1 @@

View file

@ -0,0 +1 @@
BlueWallet - محفظة Bitcoin

View file

@ -0,0 +1,10 @@
الميزات
* المصدر المفتوح
* التشفير الكامل
* الإنكار المقبول
* مرونة الرسوم
* الاستبدال بالرسوم (RBF)
* نظام SegWit (الشاهد المنفصل)
* محافظ للتحقق من الرصيد (الرصد) فقط
* شبكة Lightning

View file

@ -0,0 +1 @@
../en-US/release_notes.txt

View file

@ -0,0 +1 @@
https://github.com/BlueWallet/BlueWallet/issues

View file

@ -1,6 +1,6 @@
Store, send and receive bitcoin with the wallet focus on security and simplicity.
On BlueWallet you own you private keys. A Bitcoin wallet focused on us the users.
On BlueWallet you own your private keys. A Bitcoin wallet focused on us the users.
You can instantly transact with anyone in the world and transform the financial system right from your pocket.

View file

@ -0,0 +1 @@

View file

@ -0,0 +1,53 @@
A Bitcoin wallet that allows you to store, send Bitcoin, receive Bitcoin and buy Bitcoin with focus on security and simplicity.
On BlueWallet, a bitcoin wallet you own your private keys. A Bitcoin wallet made by Bitcoin users for the community.
You can instantly transact with anyone in the world and transform the financial system right from your pocket.
Create for free unlimited number of bitcoin wallets or import your existing wallet. It's simple and fast.
_____
Folgendes ist enthalten:
1 - Security by design
Free/Libre Open Source Software
MIT licensed, you can build it and run it on your own! Made with ReactNative
Plausible deniability
Password which decrypts fake bitcoin wallets if you are forced to disclose your access
Full encryption
On top of the iOS multi-layer encryption, we encrypt everything with added passwords
Full node
Connect to your Bitcoin full node through Electrum
Cold Storage
Connect to your hardware wallet and keep your coins in Cold storage
2 - Focused on your experience
Be in control
Private keys never leave your device.
You control your private keys
Flexible fees
Starting from 1 Satoshi. Defined by you, the user
Replace-By-Fee
(RBF) Speed-up your transactions by increasing the fee (BIP125)
Watch-only wallets
Watch-only wallets allow you to keep an eye on your cold storage without touching the hardware.
Lightning Network
Lightning wallet with zero-configuration. Unfairly cheap and fast transactions with the best Bitcoin user experience.
Bitcoin kaufen
Enter in the open financial revolution with the ability to buy Bitcoin directly in your wallet.
Local Trader
A p2p Bitcoin Trading platform, that allows you to buy and sell bitcoin directly to other users without 3rd parties.

View file

@ -0,0 +1 @@

View file

@ -0,0 +1 @@
BlueWallet - Bitcoin Wallet

View file

@ -0,0 +1 @@
http://www.bluewallet.io/privacy.txt

View file

@ -0,0 +1,10 @@
Eigenschaften
* Quelloffene
* Vollverschlüsselung
* Glaubhafte Täuschung
* Flexible Gebühren
* Gebührenübernahme
* SegWit
* Wallets beobachten
* Lightning Netzwerk

View file

@ -0,0 +1 @@
../en-US/release_notes.txt

View file

@ -0,0 +1 @@
Bitcoin & Lightning

View file

@ -0,0 +1 @@
https://github.com/BlueWallet/BlueWallet/issues

View file

@ -1,6 +1,6 @@
A Bitcoin wallet that allows you to store, send Bitcoin, receive Bitcoin and buy Bitcoin with focus on security and simplicity.
On BlueWallet, a bitcoin wallet you own you private keys. A Bitcoin wallet made by Bitcoin users for the community.
On BlueWallet, a bitcoin wallet you own your private keys. A Bitcoin wallet made by Bitcoin users for the community.
You can instantly transact with anyone in the world and transform the financial system right from your pocket.

View file

@ -1,3 +1,28 @@
v5.5.7
======
* ADD: Split for large devices
* ADD: Export file option
* ADD: Slovenian & Hebrew language
* ADD: KES, NGN, TWD Fiat
* FIX: After broadcast, value of sent transaction is incorrect
* FIX: transaction time when using ElectrumPersonalServer
* FIX: some ElectrumPersonalServer txs stay unconfirmed
* FIX: Fixed dark/light theme switch
* FIX: updated languages pt_BR, pt_PT, es_ES, sl_SI, de_DE, fr_FR, cs_CZ, ja_JP, it, ru, he
* FIX: create-ln-invoice - button is not blocked, which can lead to multiple invoices created
* FIX: If user has approved notifications, send hash from watch
* FIX: If Camera not authorized, show Alert.
* FIX: Only show direct export
* FIX: better handling of errors from HodlHodl
* FIX: Safello buy-bitcoin flow
* FIX: Request location authorization for HodlHodl
* FIX: Settings|Network|Lightning Settings fails input for leading or trailing blank
* FIX: show Import file option
* FIX: Background had wrong color during loading phase
* REF: speeded up large wallets (>3k txs)
* REF: speedup onchain wallet creation
v5.5.6
======
@ -51,19 +76,3 @@ v5.5.2
* FIX: missing text on sendMAX dialog box confirmation
* FIX: Invoice creation darkmode
* FIX: Font-Color in Input Field "transaction notes" is not adapted for dark mode
v5.5.1
======
* ADD: Dark Mode
* FIX: #1309 Sharing doesnt include text
* FIX: import mnemonic w/typo, 'try again' does nothing (closes #1286)
* ADD: support importing watch-only in bitcoincore format [fingerprint/derivation]xpub (wallet descriptors)
* REF: dont store txhex in transactions list
* REF: wrap save storage in try-catch
v5.5.0
======
* FIX: provide entropy for D20
* REF: speeded up transactions list

View file

@ -0,0 +1,53 @@
ארנק ביטקוין המאפשר לך לאחסן, לשלוח ביטקוין, לקבל ביטקוין ולקנות ביטקוין תוך שמירה על אבטחה ופשטות.
בארנק BlueWallet, המפתחות שלך נשלטים על ידך. ארנק ביטקוין שנוצר על ידי הקהילה למען הקהילה.
You can instantly transact with anyone in the world and transform the financial system right from your pocket.
Create for free unlimited number of bitcoin wallets or import your existing wallet. It's simple and fast.
_____
Here's what you get:
1 - Security by design
Open Source
MIT licensed, you can build it and run it on your own! Made with ReactNative
Plausible deniability
Password which decrypts fake bitcoin wallets if you are forced to disclose your access
Full encryption
On top of the iOS multi-layer encryption, we encrypt everything with added passwords
Full node
Connect to your Bitcoin full node through Electrum
Cold Storage
Connect to your hardware wallet and keep your coins in Cold storage
2 - Focused on your experience
Be in control
Private keys never leave your device.
You control your private keys
Flexible fees
Starting from 1 Satoshi. Defined by you, the user
Replace-By-Fee
(RBF) Speed-up your transactions by increasing the fee (BIP125)
Watch-only wallets
Watch-only wallets allow you to keep an eye on your cold storage without touching the hardware.
Lightning Network
Lightning wallet with zero-configuration. Unfairly cheap and fast transactions with the best Bitcoin user experience.
Buy Bitcoin
Enter in the open financial revolution with the ability to buy Bitcoin directly in your wallet.
Local Trader
A p2p Bitcoin Trading platform, that allows you to buy and sell bitcoin directly to other users without 3rd parties.

View file

@ -0,0 +1 @@
ביטקוין,ארנק,ארנק ביטקוין,בלוקצ'יין,btc,בק,ביט,מטבע קריפטו,קריפטו,קניית ביטקוין,אלקטרום,איתריום

View file

@ -0,0 +1 @@

View file

@ -0,0 +1,10 @@
תכונות
* קוד פתוח
* הצפנה מלאה
* הכחשה סבירה (Plausible deniability)
* עמלות גמישות
* Replace-By-Fee (RBF)
* SegWit
* ארנקי צפייה בלבד
* רשת הברק

View file

@ -0,0 +1 @@
../en-US/release_notes.txt

View file

@ -0,0 +1 @@
https://github.com/BlueWallet/BlueWallet/issues

View file

@ -0,0 +1,53 @@
Un wallet per Bitcoin che ti consente di conservare, inviare, ricevere ed acquistare Bitcoin in modo sicuro e semplice.
Su BlueWallet, un wallet Bitcoin, tu solo sei in controllo delle tue chiavi private. Un wallet Bitcoin fatto da utilizzatori per la comunità Bitcoin.
Puoi scambiare valore instantaneamente con chiunque nel mondo e trasformare il sistema finanziario direttamente dalla tua tasca.
Crea gratis un numero illimitato di wallet bitcoin o importa il tuo wallet. Semplice e veloce.
_____
Ecco quel che ottieni:
1 - Sicurezza by design
Open Source
Licenza MIT, puoi compilarlo ed eseguirlo per conto tuo! Scritto con ReactNative
Negabilità plausibile
Una password che sblocca falsi wallet bitcoin se sei costretto a rivelare i tuoi accessi
Crittografia completa
In aggiunta alla crittografia multi strato di iOS, codifichiamo tutto con password aggiuntive
Full node
Si connette al tuo full node Bitcoin tramite Electrum
Cold Storage
Connettiti al tuo hardware wallet e conserva i tuoi coins al sicuro.
2 - Focalizzato sulla tua esperienza
Mantieni il controllo
Le chiavi private non lasciano mai il tuo dispositivo.
Tu controlli le chiavi private.
Commissioni flessibili
A partire da 1 Satoshi. Deciso da te, l'utente
Replace-By-Fee
(RBF) Velocizza le tue transazioni aumentando le commissioni (BIP125)
Watch-only wallets
Watch-only wallets allow you to keep an eye on your cold storage without touching the hardware.
Lightning Network
Wallet per Lightning Network con zero configurazioni necessarie. Per transazioni incredibilmente economiche e veloci con la miglior esperienza utente.
Acquista Bitcoin
Enter in the open financial revolution with the ability to buy Bitcoin directly in your wallet.
Local Trader
A p2p Bitcoin Trading platform, that allows you to buy and sell bitcoin directly to other users without 3rd parties.

View file

@ -0,0 +1 @@
bitcoin,wallet,bitcoin wallet,blockchain,btc,cryptocurrency,acquista bitcoin,electrum,crypto-valuta

View file

@ -0,0 +1 @@

View file

@ -0,0 +1,10 @@
Funzionalità
* OpenSource
* Crittografia completa
* Negabilita' plausibile
* Commissioni flessibili
* Replace-By-Fee
* SegWit
* Watch-only wallets
* Lightning network

View file

@ -0,0 +1 @@
../en-US/release_notes.txt

View file

@ -0,0 +1 @@
https://github.com/BlueWallet/BlueWallet/issues

View file

@ -1,6 +1,6 @@
Store, send and receive bitcoin with the wallet focus on security and simplicity.
On BlueWallet you own you private keys. A Bitcoin wallet focused on us the users.
On BlueWallet you own your private keys. A Bitcoin wallet focused on us the users.
You can instantly transact with anyone in the world and transform the financial system right from your pocket.

View file

@ -1 +1 @@
MZGenre.Finance
FINANCE

View file

@ -1,54 +1,53 @@
Биткоин кошелек для iOS на русском языке.
Бесплатный и с открытым исходным кодом.
Биткойн-кошелёк который позволяет вам получать, хранить, посылать и покупать биткойны просто и безопасно.
* Приватные ключи всегда остаются на вашем устройстве
* Полное шифрование
* Фальшивый пароль для расшифровки фальшивых кошельков (Правдоподобное отрицание)
* Поддержка SegWit
* Поддержка замены транзакций (Replace-by-Fee - RBF)
* Watch-only (Sentinel) wallets
В BlueWallet вы владеете приватными ключами. Этокошелек, созданный пользователями биткойнов для сообщества.
Вы можете моментально обмениваться средствами с кем угодно в мире. Новая финансовая система у вас в кармане.
Создайте бесплатно неограниченное количество биткойн-кошельков или импортируйте существующий. Это просто и быстро.
_____
Что внутри:
Особенности
===========
1 - Разработка с фокусом на безопасность
Открытый исходный код
---------------------
Лицензия MIT. Вы можете самостоятельно собрать приложение. Сделано на ReactNative
Лицензия MIT даёт максимум свободы модифицировать код! Разработано на ReactNative
Безопасность под контролем
--------------------------
Приватные ключи всегда остаются на вашем устройстве
Правдопободное отрицание (Plausible deniability)
В случае угрозы вы можете раскрыть пароль от фальшивого биткойн-кошелька. Ваши биткоины будут скрыты от посторонних
Полное шифрование
----------------
Поверх родного многослойного шифрования iOS, BlueWallet дополнительно шифрует все данные пользовательским паролем.
Не стоит доверять биометрической безопасности
Поверх родного многослойного шифрования iOS, BlueWallet дополнительно шифрует все данные пользовательским паролем
Правдопободное отрицание
-------------------
Aka Plausible Deniability. На случай если вас заставляют раскрыть пароль под давлением, вы можете раскрыть "фальшивый"
пароль от фальшивого кошелька. Ваши биткоины будут скрыты от посторонних
Поддержка полной ноды
Используйте свой сервер Биткойн с Electrum, чтобы повысить приватность
Холодное хранение
Подключите аппаратный кошелёк и храните монеты в Холодном хранилище
2 - Получите максимум возможностей
Безопасность под контролем
Приватные ключи всегда остаются на вашем устройстве
Только вы управляете ключами
Гибкие комиссии
---------------
От одного Сатоши. Не переплачивайте за биткоин переводы
Замена транзакций
------------------
Aka Replace-By-Fee (RBF). Вы можете ускорить ваши подвисшие транзакции повышением комиссии (стандарт BIP125).
Вы также можете поменять адрес назначения для неподтвержденной транзакции
Замена транзакций (Replace-By-Fee)
Вы можете ускорить ваши подвисшие транзакции повышением комиссии (стандарт BIP125). Вы также можете поменять адрес назначения для неподтвержденной транзакции
SegWit
--------
Поддержка SegWit (в режиме P2SH совместимости). Экономьте на комиссии еще больше
Кошельки только для просмотра (Watch-only)
Импортировав адрес своего Холодного хранилища вы можете следить за его балансом не подвергая его риску.
Экспорт TXHEX
--------------
Получите данные транзакции не выпуская транзакцию в сеть (если хотите транслировать транакцию самостоятельно)
Lightning Network
Этот платёжный протокол позволяет проводить моментальные и почти бесплатные платежи!
Покупайте Биткойн
Прямо в вашем кошельке - это настоящая финансовая революция.
Watch-only (Sentinel) wallets
-----------------------------
You can watch status of your offline wallets. No private keys on your device!
Local Trader
Покупайте и продавайте биткоины напрямую с другим пользователям, без третьих лиц.

View file

@ -1,10 +1,7 @@
Features
Особенности
* Open Source
* Full encryption
* Plausible deniability
* Flexible fees
* Replace-By-Fee (RBF)
* SegWit
* Watch-only (Sentinel) wallets
* Открытый исходный код
* Все данные шифруются
* Правдоподобное отрицание
* Гибкие настройки комиссии
* Lightning network

View file

@ -1 +1 @@
MZGenre.Apps.Shopping
SHOPPING

View file

@ -1,6 +1,6 @@
Store, send and receive bitcoin with the wallet focus on security and simplicity.
On BlueWallet you own you private keys. A Bitcoin wallet focused on us the users.
On BlueWallet you own your private keys. A Bitcoin wallet focused on us the users.
You can instantly transact with anyone in the world and transform the financial system right from your pocket.

366
loc/ar.json Normal file
View file

@ -0,0 +1,366 @@
{
"_": {
"bad_password": "كلمة المرور غير صحيحة، يُرجى إعادة المحاولة.",
"cancel": "الإلغاء",
"continue": "المتابعة",
"enter_password": "إدخال كلمة المرور",
"never": "أبدًا",
"of": "{number} من {total}",
"ok": "موافق",
"storage_is_encrypted": "وحدة التخزين الخاصة بك مشفرة. أنت بحاجة إلى كلمة المرور لفك تشفيرها",
"yes": "نعم"
},
"azteco": {
"codeIs": "رمز القسيمة الخاص بك هو",
"errorBeforeRefeem": "يجب عليك إضافة محفظة Bitcoin أولًا قبل الاسترداد.",
"errorSomething": "حدث خطأ. هل ما زالت هذه القسيمة صالحة؟",
"redeem": "الاسترداد إلى المحفظة",
"redeemButton": "الاسترداد",
"success": "نجح الاسترداد",
"title": "استرداد قسيمة Azte.co"
},
"entropy": {
"save": "الحفظ",
"title": "الإنتروبيا (العشوائية)",
"undo": "تراجع"
},
"errors": {
"broadcast": "فشل البث",
"error": "خطأ",
"network": "خطأ في الشبكة"
},
"hodl": {
"are_you_sure_you_want_to_logout": "هل أنت متأكد أنك تريد تسجيل الخروج من HodlHodl؟",
"cont_address_escrow": "الضمان",
"cont_address_to": "إلى",
"cont_buying": "الشراء",
"cont_cancel": "إلغاء العقد",
"cont_cancel_q": "هل أنت متأكد أنك تريد إلغاء هذا العقد؟",
"cont_cancel_y": "نعم، إلغاء العقد",
"cont_chat": "فتح الدردشة مع الطرف المقابل",
"cont_how": "كيفية الدفع",
"cont_no": "ليس لديك أي عقود قيد التنفيذ",
"cont_paid": "وضع علامة على العقد كمدفوع",
"cont_paid_e": "لا تفعل ذلك إلا إذا أرسلت أموالًا إلى البائع عبر وسيلة الدفع المتفق عليها",
"cont_paid_q": "هل أنت متأكد أنك تريد وضع علامة على هذا العقد كمدفوع؟",
"cont_selling": "البيع",
"cont_st_completed": "انتهى كل شيء!",
"cont_st_in_progress_buyer": "العملات في الضمان، يُرجى الدفع للبائع",
"cont_st_paid_enought": "عملات Bitcoin في الضمان! يُرجى الدفع للبائع\nعبر وسيلة الدفع المتفق عليها",
"cont_st_paid_waiting": "في انتظار تحرير البائع للعملات من الضمان",
"cont_st_waiting": "في انتظار إيداع البائع لعملات Bitcoin في الضمان...",
"cont_title": "عقودي",
"filter_any": "أي عقد",
"filter_buying": "الشراء",
"filter_country_global": "العروض الشاملة",
"filter_country_near": "بالقرب مني",
"filter_currency": "العملة",
"filter_detail": "التفاصيل",
"filter_filters": "المرشحات",
"filter_iambuying": "أنا أشتري Bitcoin",
"filter_iamselling": "أنا أبيع Bitcoin",
"filter_method": "وسيلة الدفع",
"filter_search": "البحث",
"filter_selling": "البيع",
"item_minmax": "الحد الأدنى/الحد الأقصى",
"item_nooffers": "لا توجد عروض. جرِّب تغيير \"بالقرب مني\" إلى العروض الشاملة!",
"item_rating": "تداولات {rating}",
"item_rating_no": "لا يوجد تقييم",
"login": "تسجيل الدخول",
"mycont": "عقودي",
"offer_accept": "قبول العرض",
"offer_account_finish": "يبدو أنك لم تنته من إعداد حساب على HodlHodl، هل ترغب في إنهاء الإعداد الآن؟",
"offer_choosemethod": "اختيار وسيلة الدفع",
"offer_confirmations": "التأكيدات",
"offer_minmax": "الحد الأدنى/الحد الأقصى",
"offer_minutes": "الحد الأدنى",
"offer_promt_fiat": "ما مقدار {currency} التي تريد شراءها؟",
"offer_promt_fiat_e": "100 على سبيل المثال",
"offer_window": "الإطار الزمني",
"p2p": "تبادل من نظير إلى نظير"
},
"lnd": {
"errorInvoiceExpired": "انتهت صلاحية الفاتورة",
"exchange": "التبادل",
"expired": "منتهية الصلاحية",
"expiredLow": "انتهت صلاحيتها",
"expiresIn": "سينتهي بعد: {time}",
"payButton": "الدفع",
"placeholder": "فاتورة",
"potentialFee": "الرسوم المحتملة: {fee}",
"refill": "إعادة التعبئة",
"refill_card": "إعادة التعبئة باستخدام البطاقة البنكية",
"refill_create": "للمتابعة، يُرجى إنشاء محفظة Bitcoin لإعادة التعبئة باستخدامها.",
"refill_external": "إعادة التعبئة باستخدام محفظة خارجية",
"refill_lnd_balance": "إعادة تعبئة رصيد محفظة Lightning",
"sameWalletAsInvoiceError": "لا يمكنك دفع فاتورة بنفس المحفظة المستخدمة لإنشائها.",
"title": "إدارة الأموال"
},
"lndViewInvoice": {
"additional_info": "معلومة إضافية",
"for": "إلى:",
"has_been_paid": "تم دفع هذه الفاتورة إلى",
"open_direct_channel": "فتح قناة مباشرة مع هذه العقدة:",
"please_pay": "يُرجى الدفع",
"preimage": "الصورة الأصلية",
"sats": "بالساتوشي",
"wasnt_paid_and_expired": "لم يتم دفع هذه الفاتورة وانتهت صلاحيتها"
},
"plausibledeniability": {
"create_fake_storage": "إنشاء وحدة تخزين مشفرة",
"create_password": "إنشاء كلمة مرور",
"create_password_explanation": "يجب ألا تتطابق كلمة المرور لوحدة التخزين المزيفة مع كلمة المرور لوحدة التخزين الرئيسية",
"help": "قد تُضطر في ظل ظروف معينة إلى الكشف عن كلمة مرور. للحفاظ على عملاتك آمنة، يمكن لمحفظة BlueWallet إنشاء وحدة تخزين مشفرة أخرى بكلمة مرور مختلفة. ويمكنك الكشف عن كلمة المرور هذه لطرف خارجي في حال التعرض لأي ضغوط. إذا تم إدخالها في BlueWallet، فسيتم فتح وحدة تخزين \"مزيفة\" جديدة. سيبدو ذلك قانونيًا للطرف الخارجي، لكنه سيحافظ سرًا على أمان وحدة تخزينك الرئيسية التي تحتفظ فيها بالعملات المعدنية.",
"help2": "ستعمل وحدة التخزين الجديدة بكامل طاقتها، ويمكنك تخزين بعض الحدود الدنيا من المبالغ هناك بحيث تبدو أكثر مصداقية.",
"password_should_not_match": "كلمة المرور قيد الاستخدام حاليًا. يُرجى تجربة كلمة مرور مختلفة.",
"passwords_do_not_match": "كلمات المرور غير متطابقة، حاول مرة أخرى",
"retype_password": "إعادة إدخال كلمة المرور",
"success": "نجحت العملية",
"title": "الإنكار المقبول"
},
"pleasebackup": {
"ask": "هل حفظت العبارة الاحتياطية لمحفظتك؟ ستحتاج إلى هذه العبارة الاحتياطية للوصول إلى أموالك في حالة فقدانك لهذا الجهاز. ودون العبارة الاحتياطية، ستفقد أموالك للأبد.",
"ask_no": "لا، لم أفعل",
"ask_yes": "نعم، لقد فعلت",
"ok": "حسنًا، لقد دوَّنتها!",
"ok_lnd": "حسنًا، لقد حفظتها.",
"text": "يُرجى أخذ لحظة من وقتك لكتابة هذه العبارة التذكيرية على قصاصة ورقية. إنها وسيلتك الاحتياطية لاستعادة المحفظة على جهاز آخر.",
"text_lnd": "يُرجى أخذ لحظة من وقتك لحفظ مصادقة LNDHub هذه. إنها وسيلتك الاحتياطية لاستعادة المحفظة على جهاز آخر.",
"title": "تم إنشاء محفظتك..."
},
"receive": {
"details_create": "الإنشاء",
"details_label": "الوصف",
"details_setAmount": "استلام مبلغ",
"details_share": "المشاركة",
"header": "الاستلام"
},
"send": {
"broadcastButton": "البث",
"broadcastError": "خطأ",
"broadcastNone": "إدخال معرِّف المعاملة",
"broadcastPending": "قيد الانتظار",
"broadcastSuccess": "نجح الإرسال",
"confirm_header": "التأكيد",
"confirm_sendNow": "إرسال الآن",
"create_amount": "المبلغ",
"create_broadcast": "البث",
"create_copy": "النسخ الآن والبث لاحقًا",
"create_details": "التفاصيل",
"create_fee": "الرسوم",
"create_memo": "مذكرة",
"create_satoshi_per_byte": "ساتوشي لكل بايت",
"create_this_is_hex": "هذا هو التنسيق السداسي لمعاملتك، موقَّع وجاهز للبث على الشبكة.",
"create_to": "إلى",
"create_tx_size": "حجم المعاملة",
"create_verify": "التحقق على coinb.in",
"details_add_rec_add": "إضافة المستلم",
"details_add_rec_rem": "إزالة المستلم",
"details_address": "العنوان",
"details_address_field_is_not_valid": "حقل العنوان غير صالح",
"details_adv_fee_bump": "السماح بزيادة الرسوم",
"details_adv_full": "استخدام الرصيد الكامل",
"details_adv_full_remove": "ستتم إزالة المستلمين الآخرين من هذه المعاملة.",
"details_adv_full_sure": "هل أنت متأكد أنك تريد استخدام الرصيد الكامل لمحفظتك لهذه المعاملة؟",
"details_adv_import": "استيراد المعاملة",
"details_amount_field_is_not_valid": "حقل المبلغ غير صالح",
"details_create": "إنشاء فاتورة",
"details_error_decode": "خطأ: يتعذَّر فك تشفير عنوان Bitcoin",
"details_fee_field_is_not_valid": "حقل الرسوم غير صالح",
"details_next": "التالي",
"details_no_maximum": "لا تدعم المحفظة المحدَّدة الحساب التلقائي للحد الأقصى للرصيد. هل أنت متأكد أنك تريد اختيار هذه المحفظة؟",
"details_no_multiple": "لا تدعم المحفظة المحدَّدة إرسال Bitcoin إلى عدة مستلمين. هل أنت متأكد أنك تريد اختيار هذه المحفظة؟",
"details_no_signed_tx": "لا يحتوي الملف المحدَّد على معاملة موقَّعة يمكن استيرادها.",
"details_note_placeholder": "ملاحظة شخصية",
"details_scan": "المسح الضوئي",
"details_total_exceeds_balance": "مبلغ الإرسال يتجاوز الرصيد المتاح.",
"details_wallet_before_tx": "يجب عليك إضافة محفظة Bitcoin أولًا قبل إنشاء معاملة.",
"details_wallet_selection": "اختيار المحفظة",
"dynamic_init": "جارٍ التهيئة...",
"dynamic_next": "التالي",
"dynamic_prev": "السابق",
"dynamic_start": "البدء",
"dynamic_stop": "الإيقاف",
"header": "الإرسال",
"input_clear": "المسح",
"input_done": "تم",
"input_paste": "اللصق",
"input_total": "الإجمالي:",
"permission_camera_message": "نحتاج إلى إذنك لاستخدام الكاميرا الخاصة بك",
"permission_camera_title": "إذن باستخدام الكاميرا",
"open_settings": "فتح الإعدادات",
"permission_storage_later": "اسألني لاحقًا",
"permission_storage_message": "تحتاج BlueWallet إلى إذنك للوصول إلى وحدة التخزين الخاصة بك لحفظ هذه المعاملة.",
"permission_storage_title": "إذن وصول BlueWallet إلى وحدة التخزين",
"psbt_clipboard": "النسخ إلى الحافظة",
"psbt_this_is_psbt": "هذه معاملة Bitcoin موقَّعة جزئيًا (PSBT). يُرجى الانتهاء من توقيعها باستخدام محفظة الجهاز الخاصة بك.",
"psbt_tx_export": "التصدير إلى ملف",
"psbt_tx_open": "فتح معاملة موقَّعة",
"psbt_tx_scan": "المسح الضوئي معاملة موقَّعة",
"qr_error_no_qrcode": "تحتوي الصورة المحدَّدة على رمز استجابة سريعة.",
"qr_error_no_wallet": "لا يحتوي الملف المحدَّد على محفظة يمكن استيرادها.",
"success_done": "تم",
"txSaved": "تم حفظ ملف المعاملة ({filePath}) في مجلد \"التنزيلات\" الخاص بك."
},
"settings": {
"about": "نبذة",
"about_awesome": "تم تصميمها باستخدام الأدوات الرائعة التالية",
"about_backup": "احتفظ دائمًا بنسخة احتياطية من مفاتيحك!",
"about_free": "BlueWallet هي مشروع مجاني ومفتوح المصدر، أنشأه مستخدمو Bitcoin.",
"about_release_notes": "ملاحظات الإصدار",
"about_review": "اترك لنا مراجعتك",
"about_selftest": "تشغيل اختبار ذاتي",
"about_sm_github": "GitHub",
"about_sm_telegram": "دردشة Telegram",
"about_sm_twitter": "تابعنا على تويتر",
"advanced_options": "الخيارات المتقدمة",
"currency": "العملة",
"currency_source": "يتم الحصول على الأسعار من CoinDesk",
"default_desc": "عند تعطيل هذا الإعداد، ستفتح BlueWallet المحفظة المحدَّدة فور التشغيل.",
"default_info": "المعلومات الافتراضية",
"default_title": "عند التشغيل",
"default_wallets": "عرض جميع المحافظ",
"electrum_connected": "متصل",
"electrum_connected_not": "غير متصل",
"electrum_error_connect": "يتعذَّر الاتصال بخادم Electrum المقدَّم",
"electrum_host": "المضيف، على سبيل المثال {example}",
"electrum_port": "منفذ TCP، يكون عادةً {example}",
"electrum_port_ssl": "منفذ SSL، يكون عادةً {example}",
"electrum_saved": "تم حفظ تغييراتك بنجاح. قد تحتاج إلى إعادة التشغيل لتصبح التغييرات سارية المفعول.",
"electrum_settings": "إعدادات Electrum",
"electrum_settings_explain": "اترك الحقول فارغة لاستخدام الإعدادات الافتراضية",
"electrum_status": "الحالة",
"encrypt_decrypt": "فك تشفير وحدة التخزين",
"encrypt_decrypt_q": "هل أنت متأكد أنك تريد فك تشفير وحدة التخزين الخاصة بك؟ سيسمح إجراء ذلك بالوصول إلى محافظك دون كلمة مرور.",
"encrypt_del_uninstall": "الحذف في حال إلغاء تثبيت BlueWallet",
"encrypt_enc_and_pass": "مشفرة ومحمية بكلمة مرور",
"encrypt_title": "الأمان",
"encrypt_tstorage": "وحدة التخزين",
"encrypt_use": "استخدام {type}",
"encrypt_use_expl": "سيتم استخدام {type} لتأكيد هويتك قبل إجراء معاملة أو فتح محفظة أو تصديرها أو حذفها. ولن يتم استخدام {type} لفتح وحدة تخزين مشفرة.",
"general": "العامة",
"general_adv_mode": "الوضع المتقدم",
"general_adv_mode_e": "عند تمكين هذا الإعداد، سترى خيارات متقدمة في أثناء إنشاء المحفظة، مثل أنواع المحافظ المختلفة، والقدرة على تحديد مثيل LNDHub الذي ترغب في الاتصال به، وإنتروبيا (عشوائية) مخصصة.",
"general_continuity": "الارتباط",
"general_continuity_e": "عند تمكين هذا الإعداد، ستتمكن من عرض المحافظ والمعاملات المحدَّدة باستخدام أجهزتك الأخرى المتصلة بحساب Apple iCloud.",
"header": "الإعدادات",
"language": "اللغة",
"language_restart": "عند تحديد لغة جديدة، قد تحتاج إلى إعادة تشغيل BlueWallet لتصبح التغييرات سارية المفعول.",
"lightning_error_lndhub_uri": "معرِّف URI لبرنامج تضمين LndHub غير صالح",
"lightning_saved": "تم حفظ تغييراتك بنجاح",
"lightning_settings": "إعدادات Lightning",
"lightning_settings_explain": "للاتصال بعقدة LND الخاصة بك، يُرجى تثبيت LndHub ووضع عنوان URL له هنا في الإعدادات. اترك الحقل فارغًا لاستخدام LNDHub (lndhub.io) لمحفظة BlueWallet. ستتصل المحافظ التي يتم إنشاؤها بعد حفظ التغييرات ببرنامج تضمين LNDHub المحدَّد.",
"network": "الشبكة",
"network_broadcast": "بث المعاملة",
"network_electrum": "خادم Electrum",
"password": "كلمه المرور",
"password_explain": "أنشئ كلمة المرور التي ستستخدمها لفك تشفير وحدة التخزين",
"passwords_do_not_match": "كلمتا المرور لا تتطابقان",
"plausible_deniability": "الإنكار المقبول",
"retype_password": "إعادة إدخال كلمة المرور",
"notifications": "الإشعارات",
"save": "الحفظ",
"saved": "تم الحفظ",
"not_a_valid_uri": "معرِّف URI غير صالح",
"push_notifications": "الإشعارات الفورية",
"groundcontrol_explanation": "GroundControl هو خادم إشعارات فورية مجاني مفتوح المصدر لمحافظ Bitcoin. يمكنك تثبيت خادم GroundControl الخاص بك ووضع عنوان URL له هنا لعدم الاعتماد على البنية التحتية لمحفظة BlueWallet. اترك الحقل فارغًا لاستخدام الإعدادات الافتراضية"
},
"transactions": {
"cancel_explain": "سنستبدل هذه المعاملة بالمعاملة التي تدفع لك وتحتسب رسومًا أعلى. يلغي ذلك المعاملة بشكلٍ فعال. وهذا ما يُسمَّى RBF؛ أي الاستبدال بالرسوم.",
"cancel_no": "هذه المعاملة غير قابلة للاستبدال",
"cancel_title": "إلغاء هذه المعاملة (RBF)",
"cpfp_create": "الإنشاء",
"cpfp_exp": "سننشئ معاملة أخرى تستبدل معاملتك غير المؤكدة. وسيكون إجمالي الرسوم أعلى من رسوم المعاملة الأصلية؛ حتى يجري تعدينها بشكلٍ أسرع. وهذا ما يُسمَّى CPFP؛ أي دعم المعاملة الرئيسية بمعاملة فرعية.",
"cpfp_no_bump": "هذه المعاملة غير قابلة للتسريع",
"cpfp_title": "رسوم التسريع (CPFP)",
"details_block": "ارتفاع الكتلة",
"details_copy": "النسخ",
"details_from": "المدخل",
"details_inputs": "المدخلات",
"details_outputs": "المخرجات",
"details_received": "المستلمة",
"details_show_in_block_explorer": "العرض في مستكشف الكتل",
"details_title": "المعاملة",
"details_to": "المخرج",
"details_transaction_details": "تفاصيل المعاملة",
"enable_hw": "لا يتم استخدام هذه المحفظة بجانب محفظة الجهاز. هل ترغب في تمكين استخدام محافظ الأجهزة؟",
"list_conf": "الإعداد",
"list_title": "المعاملات",
"transactions_count": "عدد المعاملات",
"rbf_explain": "سنستبدل هذه المعاملة بالمعاملة ذات الرسوم الأعلى؛ حتى يجري تعدينها بشكلٍ أسرع. وهذا ما يُسمَّى RBF؛ أي الاستبدال بالرسوم.",
"rbf_title": "رسوم التسريع (RBF)",
"status_bump": "رسوم التسريع",
"status_cancel": "إلغاء المعاملة"
},
"wallets": {
"add_bitcoin": "Bitcoin",
"add_create": "الإنشاء",
"add_entropy_generated": "{gen} بايت من الإنتروبيا (العشوائية) المحققة",
"add_entropy_provide": "توفير الإنتروبيا (العشوائية) باستخدام النرد",
"add_entropy_remain": "{gen} بايت من الإنتروبيا (العشوائية) المحققة. سيتم الحصول على {rem} بايت المتبقية من مولِّد الأرقام العشوائية للنظام.",
"add_import_wallet": "استيراد المحفظة",
"import_file": "استيراد ملف",
"add_lightning": "Lightning",
"add_lndhub": "اتصل ببرنامج تضمين LNDHub الخاص بك",
"add_lndhub_error": "عنوان العقدة المقدَّم لا ينطوي على عقدة LNDHub صالحة.",
"add_lndhub_placeholder": "عنوان العقدة الخاص بك",
"add_or": "أو",
"add_title": "إضافة محفظة",
"add_wallet_name": "الاسم",
"add_wallet_type": "النوع",
"details_address": "العنوان",
"details_advanced": "الخيارات المتقدمة",
"details_are_you_sure": "هل أنت متأكد؟",
"details_connected_to": "متصلة بـ",
"details_del_wb": "رصيد المحفظة",
"details_del_wb_err": "لا يتطابق مبلغ الرصيد المقدَّم مع رصيد هذه المحفظة. يُرجى إعادة المحاولة",
"details_del_wb_q": "يتوفر رصيد في هذه المحفظة. قبل المتابعة، يُرجى الانتباه إلى أنك لن تتمكن من استرداد الأموال دون العبارة الأولية لهذه المحفظة. لتجنب الإزالة العارضة لهذه المحفظة، يُرجى إدخال رصيد محفظتك البالغ {balance} ساتوشي.",
"details_delete": "الحذف",
"details_delete_wallet": "حذف المحفظة",
"details_display": "العرض في قائمة المحافظ",
"details_export_backup": "التصدير/النسخ الاحتياطي",
"details_marketplace": "المتجر",
"details_master_fingerprint": "بصمة الإصبع الرئيسية",
"details_no_cancel": "لا، إلغاء",
"details_save": "الحفظ",
"details_show_xpub": "إظهار عنوان XPUB للمحفظة",
"details_title": "المحفظة",
"details_type": "النوع",
"details_use_with_hardware_wallet": "الاستخدام مع محفظة جهاز",
"details_wallet_updated": "تم تحديث المحفظة",
"details_yes_delete": "نعم، الحذف",
"export_title": "تصدير المحفظة",
"import_do_import": "الاستيراد",
"import_error": "فشل الاستيراد. يُرجى التأكد من أن البيانات المقدَّمة صالحة.",
"import_explanation": "اكتب هنا عبارتك التذكيرية أو مفتاحك الخاص أو WIF أو أي شيء لديك. ستبذل BlueWallet قصارى جهدها لتخمين التنسيق الصحيح واستيراد محفظتك",
"import_imported": "تم الاستيراد",
"import_scan_qr": "المسح الضوئي أو استيراد ملف",
"import_success": "تم استيراد محفظتك بنجاح.",
"import_title": "الاستيراد",
"list_create_a_button": "إضافة الآن",
"list_create_a_wallet": "إضافة محفظة",
"list_create_a_wallet1": "إنها مجانية ويمكنك إنشاء",
"list_create_a_wallet2": "العدد الذي تريده من المحافظ",
"list_empty_txs1": "ستظهر معاملاتك هنا",
"list_empty_txs1_lightning": "يجب استخدام محفظة Lightning في معاملاتك اليومية. الرسوم رخيصة جدًا والسرعة كبيرة حقًا.",
"list_empty_txs2": "ابدأ بمحفظتك",
"list_empty_txs2_lightning": "\nللبدء في استخدامها، اضغط على \"إدارة الأموال\" واشحن رصيدك.",
"list_header": "تمثِّل المحفظة زوجًا من المفاتيح السرية (المفتاح الخاص) وعنوان يمكنك مشاركته لاستلام العملات المعدنية.",
"list_import_error": "حدث خطأ في أثناء محاولة استيراد هذه المحفظة.",
"list_import_problem": "حدثت مشكلة في استيراد هذه المحفظة",
"list_latest_transaction": "آخر معاملة",
"list_long_choose": "اختيار صورة",
"list_long_clipboard": "النسخ من الحافظة",
"list_long_scan": "مسح رمز الاستجابة السرعة ضوئيًا",
"take_photo": "التقاط صورة",
"list_tap_here_to_buy": "شراء Bitcoin",
"list_title": "المحافظ",
"list_tryagain": "إعادة المحاولة",
"reorder_title": "إعادة ترتيب المحافظ",
"select_no_bitcoin": "لا توجد محافظ Bitcoin متاحة حاليًا.",
"select_no_bitcoin_exp": "تحتاج إلى محفظة Bitcoin لإعادة تعبئة محافظ Lightning. يُرجى إنشاء محفظة أو استيراد واحدة.",
"select_wallet": "اختيار محفظة",
"xpub_copiedToClipboard": "تم النسخ إلى الحافظة.",
"xpub_title": "عنوان XPUB للمحفظة"
}
}

View file

@ -217,7 +217,7 @@
"currency": "Currency",
"currency_source": "Prices are obtained from CoinDesk",
"default_desc": "When disabled, BlueWallet will immediately open the selected wallet at launch.",
"default_info": "Default into",
"default_info": "Default info",
"default_title": "On Launch",
"default_wallets": "View All Wallets",
"electrum_connected": "Connected",
@ -345,7 +345,7 @@
"list_empty_txs1_lightning": "Lightning wallet should be used for your daily transactions. Fees are unfairly cheap and speed is blazing fast.",
"list_empty_txs2": "Start with your wallet",
"list_empty_txs2_lightning": "\nTo start using it tap on \"manage funds\" and topup your balance.",
"list_header": "A wallet represents a pair of a secret (private key) and an addressyou can share to receive coins.",
"list_header": "A wallet represents a pair of keys, one private and one you can share to receive coins.",
"list_import_error": "An error was encountered when attempting to import this wallet.",
"list_import_problem": "There was a problem importing this wallet",
"list_latest_transaction": "latest transaction",

View file

@ -1,114 +1,366 @@
{
"_": {
"bad_password": "Contraseña incorrecta. Intente nuevamente.",
"bad_password": "Contraseña incorrecta, por favor inténtalo de nuevo.",
"cancel": "Cancelar",
"continue": "Continua",
"enter_password": "Inserte contraseña",
"enter_password": "Introduce la contraseña",
"never": "nunca",
"of": "{number} de {total}",
"ok": "OK",
"storage_is_encrypted": "Su almacenamiento está cifrado. Se requiere contraseña para descifrarla."
"storage_is_encrypted": "Tu almacenamiento está cifrado. Se requiere la contraseña para descifrarlo.",
"yes": "Sí"
},
"azteco": {
"codeIs": "El código de tu cupón es",
"errorBeforeRefeem": "Para poder canjearlo tienes que añadir una cartera de Bitcoin.",
"errorSomething": "Algo ha salido mal. ¿Sigue siendo válido este cupón?",
"redeem": "Canjear y mover a la cartera",
"redeemButton": "Canjear",
"success": "Completado",
"title": "Canjear cupón Azte.co"
},
"entropy": {
"save": "Guardar",
"title": "Entropía ",
"undo": "Deshacer"
},
"errors": {
"broadcast": "Error de transmisión",
"error": "Error",
"network": "Error de Red"
},
"hodl": {
"are_you_sure_you_want_to_logout": "¿Estás seguro de que quieres cerrar sesión en HodlHodl?",
"cont_address_escrow": "Depósito",
"cont_address_to": "Para",
"cont_buying": "Comprando",
"cont_cancel": "Cancelar contrato",
"cont_cancel_q": "¿Estás seguro de que quieres cancelar este contrato?",
"cont_cancel_y": "Sí, cancelar contrato",
"cont_chat": "Iniciar un conversación con la contraparte",
"cont_how": "Cómo pagar",
"cont_no": "No tienes ningún contrato en curso",
"cont_paid": "Marcar contrato como pagado",
"cont_paid_e": "Haz esto solo si has enviado fondos al vendedor a través del método de pago acordado",
"cont_paid_q": "¿Estás seguro de que quieres marcar este contrato como pagado?",
"cont_selling": "en venta",
"cont_st_completed": "¡Todo listo!",
"cont_st_in_progress_buyer": "Las monedas están en depósito, pague al vendedor",
"cont_st_paid_enought": "¡Los bitcoins están en el depósito! Paga al vendedor\na través del método de pago acordado",
"cont_st_paid_waiting": "Esperando a que el vendedor libere monedas del depósito en garantía",
"cont_st_waiting": "Esperando a que el vendedor deposite bitcoins en el depósito en garantía...",
"cont_title": "Mis contratos",
"filter_any": "Algo",
"filter_buying": "Comprando",
"filter_country_global": "Ofertas globales",
"filter_country_near": "Cerca de mí",
"filter_currency": "Moneda",
"filter_detail": "Detalle",
"filter_filters": "Filtros",
"filter_iambuying": "Estoy comprando bitcoin",
"filter_iamselling": "Estoy vendiendo bitcoin",
"filter_method": "Método de pago",
"filter_search": "Buscar",
"filter_selling": "Vendiendo",
"item_minmax": "Mín/Máx",
"item_nooffers": "No hay ofertas. ¡Prueba a cambiar \"Cerca de mí\" a \"Ofertas globales\"!",
"item_rating": "{rating} operaciones",
"item_rating_no": "Sin valoraciones",
"login": "Login",
"mycont": "Mis contratos",
"offer_accept": "Aceptar oferta",
"offer_account_finish": "Parece que no terminó de configurar la cuenta en HodlHodl, ¿le gustaría finalizar la configuración ahora?",
"offer_choosemethod": "Elige el método de pago",
"offer_confirmations": "Confirmaciones",
"offer_minmax": "mín / máx",
"offer_minutes": "mín",
"offer_promt_fiat": "¿Cuánto {currency} quieres comprar?",
"offer_promt_fiat_e": "Por ejemplo: 100",
"offer_window": "ventana",
"p2p": "Una casa de cambio p2p"
},
"lnd": {
"errorInvoiceExpired": "Factura expirada",
"exchange": "Casa de cambio",
"expired": "Expirado",
"refill": "Rellenar",
"refill_lnd_balance": "Rellenar el balance de la billetera Lightning",
"expiredLow": "expirado",
"expiresIn": "Expiración: {time}",
"payButton": "Pagar",
"placeholder": "Factura",
"potentialFee": "Comisión estimada: {fee}",
"refill": "Recargar",
"refill_card": "Recargar con tarjeta bancaria ",
"refill_create": "Para continuar, por favor crea una cartera de Bitcoin con la que recargar.",
"refill_external": "Recargar con una cartera externa",
"refill_lnd_balance": "Rellenar la cartera de Lightning",
"sameWalletAsInvoiceError": "No puedes pagar una factura con la misma cartera que usaste para crearla.",
"title": "manejar fondos"
},
"lndViewInvoice": {
"additional_info": "Información adicional",
"for": "Para:",
"has_been_paid": "Esta factura ha sido pagada para",
"open_direct_channel": "Abrir un canal directo con este nodo:",
"please_pay": "Por favor, pague",
"preimage": "Preimagen",
"sats": "sats",
"wasnt_paid_and_expired": "Esta factura no se pagó y ha caducado"
},
"plausibledeniability": {
"create_fake_storage": "Crear un almacen cifrado falso",
"create_password": "Crear una contraseña",
"create_password_explanation": "La contraseña para el almacen falso no puede ser el mismo para su almacen principal.",
"help": "Bajo ciertas circunstancias, usted podría verse obligado a revelar un contraseña. Para mantener sus monedas seguras, BlueWallet puede crear otro almacenamiento cifrado, con una contraseña diferente. Bajo la presiónpuede revelar esta contraseña a un tercero. Si se ingresa en BlueWallet, desbloqueará un nuevo almacenamiento `falso`. Esto parecerá legítimo para un tercero, pero en secreto mantendrá su almacenamiento principal con monedas seguras.",
"create_password_explanation": "La contraseña para el almacenamiento falso no puede ser igual que la del principal",
"help": "Bajo ciertas circunstancias, podrías verte forzado a revelar tu contraseña. Para proteger tus fondos, BlueWallet puede crear otro almacenamiento cifrado con una contraseña diferente. Da esta otra contraseña a quien te esté obligando a hacerlo y BlueWallet mostrará un almacenamiento \"falso\" que parecerá legítimo. Así mantendrás a buen recaudo el almacenamiento con tus fondos.",
"help2": "El nuevo almacen sera completamente funcional, y puedes almacenar cantidades minimas para que sea mas creible.",
"password_should_not_match": "La contraseña para el almacen falso no puede ser el mismo para su almacen principal.",
"passwords_do_not_match": "Las contraseñas no coinciden, intente nuevamente",
"password_should_not_match": "Esta contraseña ya está en uso. Por favor, introduce una diferente.",
"passwords_do_not_match": "Las contraseñas no coinciden, inténtalo otra vez",
"retype_password": "Volver a escribir contraseña",
"success": "Exitoso",
"success": "Completado",
"title": "Negación plausible"
},
"pleasebackup": {
"ask": "¿Has guardado la frase de respaldo de tu cartera? Esta frase de respaldo es necesaria para acceder a tus fondos si pierdes este dispositivo. Sin la frase de respaldo, tus fondos se perderán permanentemente.",
"ask_no": "No, no lo he hecho",
"ask_yes": "Sí, lo he hecho",
"ok": "OK, ¡ya lo he apuntado!",
"ok_lnd": "OK, lo he guardado.",
"text": "Tómate un momento para apuntar esta frase mnemotécnica en un papel. Es lo que te permitirá restaurar la cartera en otro dispositivo.",
"text_lnd": "Por favor, anota esta autenticación del LNDHub. Actuará como una copia de seguridad que puedes usar para restaurar la cartera en otro dispositivo.",
"title": "Tu cartera ha sido creada..."
},
"receive": {
"details_share": "Compartir",
"details_create": "Crear",
"details_label": "Descripción",
"details_setAmount": "Recibir con monto",
"details_share": "compartir",
"header": "Recibir"
},
"send": {
"broadcastButton": "TRANSMITIR",
"broadcastError": "error",
"broadcastNone": "Introducir hash de transacción ",
"broadcastPending": "Pendiente",
"broadcastSuccess": "Completado",
"confirm_header": "Confirmar",
"confirm_sendNow": "Enviar ahora",
"create_amount": "Cantidad",
"create_broadcast": "Transmitir",
"create_copy": "Escanear código QR",
"create_details": "Detalles",
"create_fee": "Tasa",
"create_fee": "Comisión",
"create_memo": "Comentario",
"create_satoshi_per_byte": "satoshiPorByte",
"create_this_is_hex": "Este es representacion hex de transacción, firmado y listo para ser transmitido a la red. ¿Continuar?",
"create_to": "A",
"create_tx_size": "tamaño de TX",
"details_address": "Direccion",
"create_satoshi_per_byte": "Satoshis por byte",
"create_this_is_hex": "Esto es el HEX de tu transacción, firmado y listo para ser transmitido a la red.",
"create_to": "Dirección de destino",
"create_tx_size": "Tamaño del TX",
"create_verify": "Verificar en coinb.in",
"details_add_rec_add": "Añadir destinatario",
"details_add_rec_rem": "Borrar destinatario",
"details_address": "Dirección",
"details_address_field_is_not_valid": "La dirección no es válida",
"details_adv_fee_bump": "Permitir aumentar la comisión",
"details_adv_full": "Usar todo el balance",
"details_adv_full_remove": "Los otros destinatarios serán eliminados de esta transacción.",
"details_adv_full_sure": "¿Estás seguro de querer utilizar todo el saldo de tu cartera en esta transacción?",
"details_adv_import": "Importar transacción",
"details_amount_field_is_not_valid": "La cantidad no es válida",
"details_create": "Crear",
"details_fee_field_is_not_valid": "La tasa no es válida",
"details_note_placeholder": "comentario (para ti mismo)",
"details_scan": "Escaniar",
"details_create": "Crear factura",
"details_error_decode": "Error: No se ha podido decodificar la dirección de Bitcoin",
"details_fee_field_is_not_valid": "La comisión introducida no es válida",
"details_next": "Siguiente",
"details_no_maximum": "La cartera seleccionada no permite el cálculo automático del saldo máximo. ¿Estás seguro de querer seleccionar esta cartera?",
"details_no_multiple": "La cartera seleccionada no admite el envío de bitcoin a varios destinatarios. ¿Estás seguro de querer seleccionar esta cartera?",
"details_no_signed_tx": "El archivo seleccionado no contiene una transacción firmada que se pueda importar.",
"details_note_placeholder": "nota personal",
"details_scan": "Escanear",
"details_total_exceeds_balance": "El monto excede el balance disponible.",
"header": "enviar"
"details_wallet_before_tx": "Antes de crear una transacción debes añadir una cartera de Bitcoin.",
"details_wallet_selection": "Selección de cartera",
"dynamic_init": "Iniciando",
"dynamic_next": "Avanzar",
"dynamic_prev": "Volver",
"dynamic_start": "Empezar",
"dynamic_stop": "Detener",
"header": "Enviar",
"input_clear": "Borrar",
"input_done": "Completado",
"input_paste": "Pegar",
"input_total": "Total:",
"permission_camera_message": "Necesitamos permiso para usar la cámara",
"permission_camera_title": "Permiso para usar la cámara",
"open_settings": "Abrir configuración",
"permission_storage_later": "Pregúntame luego",
"permission_storage_message": "BlueWallet necesita permiso para acceder a su almacenamiento para poder guardar esta transacción.",
"permission_storage_title": "Permiso para acceder al almacenamiento",
"psbt_clipboard": "Copiar al portapapeles",
"psbt_this_is_psbt": "Esta transacción está parcialmente firmada (PSBT). Por favor termina de firmarla con tu cartera de hardware.",
"psbt_tx_export": "Exportar a archivo",
"psbt_tx_open": "Abrir transacción firmada",
"psbt_tx_scan": "Escanear transacción firmada",
"qr_error_no_qrcode": "La imagen seleccionada no contiene un código QR.",
"qr_error_no_wallet": "El archivo seleccionado no contiene una cartera que pueda ser importada.",
"success_done": "Completado",
"txSaved": "El archivo de la transacción ({filePath}) ha sido guardado en tu carpeta de descargas."
},
"settings": {
"about": "Sobre nosotros",
"about_awesome": "Built with the awesome",
"about_backup": "¡Guarda siempre una copia de seguridad de tus llaves!",
"about_free": "BlueWallet es un proyecto de código abierto y gratuito. Elaborado por usuarios de Bitcoin.",
"about_release_notes": "Notas de la versión",
"about_review": "Escribe una reseña",
"about_selftest": "Iniciar test local",
"about_sm_github": "GitHub",
"about_sm_telegram": "Chat de Telegram",
"about_sm_twitter": "Síguenos en Twitter",
"advanced_options": "Opciones avanzadas",
"currency": "Moneda",
"currency_source": "Los precios provienen de CoinDesk",
"default_desc": "Cuando esté deshabilitado, BlueWallet abrirá por defecto la cartera seleccionada al iniciar la aplicación.",
"default_info": "Por defecto",
"default_title": "Al iniciar",
"default_wallets": "Ver todas las carteras",
"electrum_connected": "Conectado",
"electrum_connected_not": "Desconectado",
"electrum_error_connect": "No se ha podido conectar al servidor de Electrum",
"electrum_host": "host, por ejemplo {example}",
"electrum_port": "Puerto TCP, normalmente {example}",
"electrum_port_ssl": "Puerto SSL, normalmente {example}",
"electrum_saved": "Los cambios se han guardado. Puede que se requiera reiniciar la aplicación para que tomen efecto.",
"electrum_settings": "Configuración de Electrum",
"electrum_settings_explain": "Déjalo en blanco para usar el predeterminado",
"electrum_status": "Estado",
"encrypt_decrypt": "Desencriptar almacenamiento",
"encrypt_decrypt_q": "¿Seguro que quieres desencriptar tu almacenamiento? Al hacerlo, se podrá acceder a tus carteras sin contraseña.",
"encrypt_del_uninstall": "Bórralo si desinstalas BlueWallet",
"encrypt_enc_and_pass": "Encriptada y protegida mediante contraseña",
"encrypt_title": "Seguridad",
"encrypt_tstorage": "almacenamiento",
"encrypt_use": "Usar {type}",
"encrypt_use_expl": "{type} será usado para confirmar tu identidad antes de realizar una transacción y para desbloquear, exportar o borrar una cartera. {type} no será usado para desbloquear un almacenamiento encriptado.",
"general": "General",
"general_adv_mode": "Enable advanced mode",
"header": "Ajustes",
"general_adv_mode_e": "Al activarlo podrás ver opciones avanzadas, como varios tipos de carteras, la posibilidad de especificar el LNDHub al que quieres conectar y entropía personalizada al crear una cartera.",
"general_continuity": "Continuidad",
"general_continuity_e": "Al activarlo, podrá ver las transacciones y carteras seleccionadas usando cualquiera de sus dispositivos Apple conectados a iCloud.",
"header": "ajustes",
"language": "Idioma",
"language_restart": "Al seleccionar otro idioma, será necesario reiniciar BlueWallet para mostrar los cambios.",
"lightning_error_lndhub_uri": "LndHub URI no válida",
"lightning_saved": "Sus cambios se han guardado correctamente",
"lightning_settings": "Lightning settings",
"lightning_settings_explain": "To connect to your own LND node please install LndHub and put its URL here in settings. Leave blank to use default ndHub\n (lndhub.io)",
"lightning_settings_explain": "Para conectar a tu propio nodo LND, por favor instala LndHub y escribe su URL aquí, en la pantalla de configuración. Déjalo en blanco para usar el LNDHub (lndhub.io) de BlueWallet. Las carteras creadas tras guardar los cambios se conectarán al LNDHub especificado.",
"network": "Red",
"network_broadcast": "Emitir transacción",
"network_electrum": "Servidor Electrum",
"password": "Contraseña",
"password_explain": "Crea la contraseña que usarás para descifrar el almacenamiento",
"passwords_do_not_match": "Contraseñas deben ser iguales",
"plausible_deniability": "Negación plausible...",
"plausible_deniability": "Negación plausible",
"retype_password": "Ingresa la contraseña nuevamente",
"save": "save"
"notifications": "Notificaciones",
"save": "Guardar",
"saved": "Guardado",
"not_a_valid_uri": "URI no válida",
"push_notifications": "Notificaciones push",
"groundcontrol_explanation": "GroundControl es un servidor gratuito y de código abierto de notificaciones push para carteras Bitcoin. Puedes instalar tu propio servidor de GroundControl y poner su URL aquí para no depender del de BlueWallet. Déjalo en blanco para usar el predeterminado"
},
"transactions": {
"cancel_explain": "Reemplazaremos esta transacción con la que te paga y tiene tasas más altas, lo que cancelará la transacción. A esto se le llama RBF (Replace By Fee).",
"cancel_no": "Esta transacción no se puede reemplazar",
"cancel_title": "Cancelar esta transacción (RBF)",
"cpfp_create": "Crear",
"cpfp_exp": "Crearemos otra transacción que gastará tu otra transacción aun no confirmada. La comisión total será mayor que la comisión original, lo cual debería hacer que sea minada antes. A esto se le llama CPFP (Child Pays For Parent).",
"cpfp_no_bump": "Esta transacción no se puede acelerar",
"cpfp_title": "Aumentar comisión (CPFP)",
"details_block": "Altura del bloque",
"details_copy": "Copiar",
"details_from": "De",
"details_from": "Origen",
"details_inputs": "Inputs",
"details_outputs": "Outputs",
"details_received": "Recibido",
"details_show_in_block_explorer": "Mostrar en explorador de bloques",
"details_title": "Transaccion",
"details_to": "A",
"details_to": "Destino",
"details_transaction_details": "Detalles de la transacción",
"list_title": "transacciónes"
"enable_hw": "Esta cartera no está siendo usada en conjunción con una de hardware. ¿Quieres activar el uso con carteras de hardware?",
"list_conf": "conf",
"list_title": "Transacciones",
"transactions_count": "Número de transacciones",
"rbf_explain": "Reemplazaremos esta transacción por la de mayor comisión, lo que debería hacer que sea minada en menos tiempo. A esto se le llama RBF (Replace By Fee).",
"rbf_title": "Incrementar comisión (RBF)",
"status_bump": "Aumentar comisón",
"status_cancel": "Cancelar transacción"
},
"wallets": {
"add_bitcoin": "Bitcoin",
"add_create": "Crear",
"add_import_wallet": "Importar billetera",
"add_entropy_generated": "{gen} bytes de entropía generada",
"add_entropy_provide": "Entropía mediante el lanzamiento de dados",
"add_entropy_remain": "{gen} bytes of entropía generada. Los {rem} bytes restantes serán obtenidos del generador de números aleatorios.",
"add_import_wallet": "Importar cartera",
"import_file": "Importar archivo",
"add_lightning": "Lightning",
"add_lndhub": "Conecta a tu LDNHub",
"add_lndhub_error": "La dirección proporcionada no es válida para un nodo LNDHub.",
"add_lndhub_placeholder": "la dirección de tu nodo",
"add_or": "o",
"add_title": "Añadir billetera",
"add_wallet_name": "nombre de billetera",
"add_wallet_type": "tipo de billetera",
"add_title": "Añadir cartera",
"add_wallet_name": "Nombre",
"add_wallet_type": "Tipo de cartera",
"details_address": "Dirección",
"details_advanced": "Avanzado",
"details_are_you_sure": "¿Estás seguro?",
"details_connected_to": "Conectado a",
"details_del_wb": "Balance de la cartera",
"details_del_wb_err": "El balance introducido con coincide con el balance de esta cartera. Por favor, inténtelo de nuevo.",
"details_del_wb_q": "Esta cartera tiene saldo. Antes de proceder, ten en cuenta que no podrás recuperar los fondos sin la semilla de esta cartera. Para evitar el borrado accidental, por favor introduce los {balance} satoshis que contiene esta cartera.",
"details_delete": "Eliminar",
"details_delete_wallet": "Borrar cartera",
"details_display": "mostrar en la lista de carteras",
"details_export_backup": "Exportar / Guardar",
"details_marketplace": "Marketplace",
"details_master_fingerprint": "Huella dactilar maestra",
"details_no_cancel": "No, cancelar",
"details_save": "Guardar",
"details_title": "Detalles de la billetera",
"details_show_xpub": "Mostrar el XPUB de la cartera",
"details_title": "Cartera",
"details_type": "Tipo",
"details_use_with_hardware_wallet": "Usar con cartera de hardware",
"details_wallet_updated": "Cartera actualizada",
"details_yes_delete": "Si, eliminar",
"export_title": "Exportacion de billetera",
"export_title": "exportación de cartera",
"import_do_import": "Importar",
"import_error": "No se pudo importar. ¿Es valido?",
"import_error": "Error al importar. Por favor, asegúrate de que los datos introducidos son correctos.",
"import_explanation": "Escriba aquí mnemotécnica, clave privada, WIF o cualquier cosa que tenga. BlueWallet hará todo lo posible para adivinar el formato correcto e importar su billetera.",
"import_imported": "Importado",
"import_scan_qr": "o escanear codigo QR?",
"import_success": "Exito",
"import_scan_qr": "Escanear o importar un archivo",
"import_success": "Tu cartera ha sido importada.",
"import_title": "importar",
"list_create_a_button": "Agrega ahora",
"list_create_a_wallet": "Agrega una billetera",
"list_create_a_button": "Añadir",
"list_create_a_wallet": "Añadir cartera",
"list_create_a_wallet1": "Es gratis y puedes crear",
"list_create_a_wallet2": "cuantas usted quiera",
"list_empty_txs1": "Sus transacciones aparecerán aquí,",
"list_empty_txs2": "ninguno por el momento.",
"list_create_a_wallet2": "todas las que quieras",
"list_empty_txs1": "Tus transacciones aparecerán aquí",
"list_empty_txs1_lightning": "Usa carteras Lighting para tus transacciones diarias. Tienen tasas muy bajas y una velocidad de vértigo.",
"list_empty_txs2": "Empieza con tu cartera",
"list_empty_txs2_lightning": "\nPara comenzar a usarlo, toque \"manejar fondos\" y recargue su saldo.",
"list_header": "Una cartera representa un par de llaves, una privada y una que puedes compartir para recibir fondos.",
"list_import_error": "Error al intentar importar esta cartera.",
"list_import_problem": "Ha ocurrido un problema al importar esta cartera",
"list_latest_transaction": "última transacción",
"list_long_choose": "Elegir foto",
"list_long_clipboard": "Copiar del portapapeles",
"list_long_scan": "Escanear código QR",
"take_photo": "Hacer una foto",
"list_tap_here_to_buy": "Tap here to buy Bitcoin",
"list_title": "billeteras",
"reorder_title": "Reorganizar Billetera",
"select_wallet": "Selecciona billetera",
"xpub_copiedToClipboard": "Copiado a portapapeles."
"list_title": "Carteras",
"list_tryagain": "Inténtalo otra vez",
"reorder_title": "Reorganizar carteras",
"select_no_bitcoin": "No hay carteras de Bitcoin disponibles.",
"select_no_bitcoin_exp": "Es necesaria una cartera de Bitcoin para recargar una Cartera de Lighting. Por favor, cree o importe una.",
"select_wallet": "Selecciona una cartera",
"xpub_copiedToClipboard": "Copiado a portapapeles.",
"xpub_title": "XPUB de la cartera"
}
}

View file

@ -217,7 +217,7 @@
"currency": "מטבע",
"currency_source": "מחירים מסופקים מCoinDesk",
"default_desc": "When disabled, BlueWallet will immediately open the selected wallet at launch.",
"default_info": "Default into",
"default_info": "Default info",
"default_title": "On Launch",
"default_wallets": "הראה את כל הארנקים",
"electrum_connected": "מחובר",

View file

@ -21,6 +21,9 @@ dayjs.extend(relativeTime);
strings.setLanguage(lang);
let localeForDayJSAvailable = true;
switch (lang) {
case 'ar':
require('dayjs/locale/ar');
break;
case 'ca':
require('dayjs/locale/ca');
break;
@ -78,6 +81,12 @@ dayjs.extend(relativeTime);
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;
@ -118,12 +127,13 @@ dayjs.extend(relativeTime);
})();
const strings = new Localization({
en: require('./en.json'),
ar: require('./ar.json'),
ca: require('./ca.json'),
cs_cz: require('./cs_cz.json'),
da_dk: require('./da_dk.json'),
de_de: require('./de_de.json'),
el: require('./el.json'),
en: require('./en.json'),
es: require('./es.json'),
fi_fi: require('./fi_fi.json'),
fr_fr: require('./fr_fr.json'),
@ -139,6 +149,7 @@ const strings = new Localization({
pt_pt: require('./pt_pt.json'),
ru: require('./ru.json'),
sk_sk: require('./sk_sk.json'),
sl_si: require('./sl_SI.json'),
sv_se: require('./sv_se.json'),
th_th: require('./th_th.json'),
tr_tr: require('./tr_tr.json'),

View file

@ -217,7 +217,7 @@
"currency": "通貨",
"currency_source": "Prices are obtained from CoinDesk",
"default_desc": "When disabled, BlueWallet will immediately open the selected wallet at launch.",
"default_info": "Default into",
"default_info": "Default info",
"default_title": "On Launch",
"default_wallets": "View All Wallets",
"electrum_connected": "Connected",

View file

@ -1,6 +1,7 @@
export const AvailableLanguages = Object.freeze([
{ label: 'English', value: 'en' },
{ label: 'Afrikaans (AFR)', value: 'zar_afr' },
{ label: 'Arabic (AR)', value: 'ar' },
{ label: 'Català (CA)', value: 'ca' },
{ label: 'Chinese (ZH)', value: 'zh_cn' },
{ label: 'Chinese (TW)', value: 'zh_tw' },
@ -21,7 +22,8 @@ export const AvailableLanguages = Object.freeze([
{ label: 'Português (BR)', value: 'pt_br' },
{ label: 'Português (PT)', value: 'pt_pt' },
{ label: 'Русский (RU)', value: 'ru' },
{ label: 'Slovensky (SK)', value: 'sk_sk' },
{ label: 'Slovenský (SK)', value: 'sk_sk' },
{ label: 'Slovenščina (SL)', value: 'sl_si' },
{ label: 'Suomi (FI)', value: 'fi_fi' },
{ label: 'Svenska (SE)', value: 'sv_se' },
{ label: 'Thai (TH)', value: 'th_th' },

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

@ -216,7 +216,7 @@
"currency": "Mena",
"currency_source": "Prices are obtained from CoinDesk",
"default_desc": "When disabled, BlueWallet will immediately open the selected wallet at launch.",
"default_info": "Default into",
"default_info": "Default info",
"default_title": "On Launch",
"default_wallets": "Zobraziť všetky peňaženky",
"electrum_connected": "Connected",

366
loc/sl_SI.json Normal file
View file

@ -0,0 +1,366 @@
{
"_": {
"bad_password": "Napačno geslo, prosimo poskusite ponovno.",
"cancel": "Prekliči",
"continue": "Nadaljuj",
"enter_password": "Vnesite geslo",
"never": "nikoli",
"of": "{number} od {total}",
"ok": "OK",
"storage_is_encrypted": "Shramba je šifrirana. Za dešifriranje je potrebno geslo",
"yes": "Da"
},
"azteco": {
"codeIs": "Koda vašega bona je",
"errorBeforeRefeem": "Preden lahko unovčite, morate najprej dodati Bitcoin denarnico.",
"errorSomething": "Nekaj je šlo narobe. Ali je ta bon še vedno veljaven?",
"redeem": "Unovčite v denarnici",
"redeemButton": "Unovčite",
"success": "Uspešno",
"title": "Unovčite Azte.co bon"
},
"entropy": {
"save": "Shrani",
"title": "Entropija",
"undo": "Razveljavi"
},
"errors": {
"broadcast": "Objava v omrežje neuspešna",
"error": "Napaka",
"network": "Omrežna napaka"
},
"hodl": {
"are_you_sure_you_want_to_logout": "Ali ste prepričani, da se želite odjaviti iz HodlHodl?",
"cont_address_escrow": "Escrow hramba",
"cont_address_to": "Za",
"cont_buying": "kupujem",
"cont_cancel": "Preklic pogodbe",
"cont_cancel_q": "Ali ste prepričani, da želite preklicati to pogodbo?",
"cont_cancel_y": "Da, prekliči pogodbo",
"cont_chat": "Odprite klepet",
"cont_how": "Kako plačati",
"cont_no": "Nimate aktivnih pogodb",
"cont_paid": "Označi pogodbo kot plačano",
"cont_paid_e": "Naredite to le, če ste prodajalcu poslali sredstva preko dogovorjene plačilne metode",
"cont_paid_q": "Ali ste prepričani, da želite označiti to pogodbo kot plačano?",
"cont_selling": "prodajam",
"cont_st_completed": "Končano!",
"cont_st_in_progress_buyer": "Kovanci so v escrow hrambi, prosim plačajte prodajalcu",
"cont_st_paid_enought": "Bitcoini so v escrow hrambi! Prosim plačajte prodajalcu\npreko dogovorjene plačilne metode",
"cont_st_paid_waiting": "Počakajte, da prodajalec sprosti kovance iz escrow hrambe",
"cont_st_waiting": "Počakajte, da prodajalec izvede polog bitcoinov v escrow hrambo...",
"cont_title": "Moje pogodbe",
"filter_any": "Vse",
"filter_buying": "Kupujem",
"filter_country_global": "Globalne ponudbe",
"filter_country_near": "Blizu mene",
"filter_currency": "Valuta",
"filter_detail": "Podrobno",
"filter_filters": "Filtriraj",
"filter_iambuying": "Kupujem bitcoin",
"filter_iamselling": "Prodajam bitcoin",
"filter_method": "Plačilna metoda",
"filter_search": "Išči",
"filter_selling": "Prodajam",
"item_minmax": "Min/Max",
"item_nooffers": "Ni ponudb. Poskusite spremeniti \"Blizu mene\" v Globalne ponudbe!",
"item_rating": "{rating} poslov",
"item_rating_no": "Brez ocene",
"login": "Prijava",
"mycont": "Moje pogodbe",
"offer_accept": "Sprejmi ponudbo",
"offer_account_finish": "Videti je, da še niste končali registracije računa na HodlHodl, ali želite končati registracijo zdaj?",
"offer_choosemethod": "Izberite plačilno metodo",
"offer_confirmations": "potrditev",
"offer_minmax": "min / max",
"offer_minutes": "min",
"offer_promt_fiat": "Koliko {currency} želite kupiti?",
"offer_promt_fiat_e": "Na primer 100",
"offer_window": "okno",
"p2p": "P2P menjalnica"
},
"lnd": {
"errorInvoiceExpired": "Račun je potekel",
"exchange": "Menjalnica",
"expired": "Potekel",
"expiredLow": "potekel",
"expiresIn": "Poteče: {time}",
"payButton": "Plačaj",
"placeholder": "Račun",
"potentialFee": "Morebitna omrežnina: {fee}",
"refill": "Napolni",
"refill_card": "Napolni z bančno kartico",
"refill_create": "Če želite nadaljevati, ustvarite Bitcoin denarnico, s katero boste napolnili.",
"refill_external": "Napolni z zunanjo denarnico",
"refill_lnd_balance": "Napolni stanje Lightning denarnice",
"sameWalletAsInvoiceError": "Plačilo računa ni mogoče z denarnico, s katero je bil ustvarjen.",
"title": "upravljanje sredstev"
},
"lndViewInvoice": {
"additional_info": "Dodatne Informacije",
"for": "Za:",
"has_been_paid": "Ta račun je bil plačan",
"open_direct_channel": "Odpri neposreden kanal s tem vozliščem:",
"please_pay": "Prosim plačajte",
"preimage": "Preimage",
"sats": "sats",
"wasnt_paid_and_expired": "Ta račun ni bil plačan in je potekel"
},
"plausibledeniability": {
"create_fake_storage": "Ustvari Šifrirano shrambo",
"create_password": "Ustvarite geslo",
"create_password_explanation": "Geslo za lažno shrambo se ne sme ujemati z geslom glavne shrambe",
"help": "V določenih okoliščinah boste morda prisiljeni razkriti geslo. Da zavarujete vaša sredstva, lahko BlueWallet ustvari dodatno šifrirano shrambo z drugačnim geslom. Pod prisilo, lahko to geslo razkrijete tretji osebi. Če ga vnesete v BlueWallet, bo odklenilo 'lažno' shrambo. To se bo tretji osebi zdelo verodostojno, vaša skrivna glavna shramba s pravimi sredstvi pa bo ostala varna.",
"help2": "Nova shramba bo popolnoma funkcionalna, za večjo verodostojnost lahko tam hranite manjši znesek.",
"password_should_not_match": "Geslo je trenutno v uporabi. Prosimo, poskusite z drugim geslom.",
"passwords_do_not_match": "Gesli se ne ujemata, poskusite ponovno",
"retype_password": "Ponovno vpišite geslo",
"success": "Uspešno",
"title": "Verodostojno Zanikanje"
},
"pleasebackup": {
"ask": "Ali ste shranili varnostno kopijo (seznam besed) vaše denarnice? Varnostna kopija je potrebna za dostop do vaših sredstev v primeru izgube naprave. Brez varnostne kopije bodo vaša sredstva trajno izgubljena.",
"ask_no": "Ne, nisem",
"ask_yes": "Da, sem",
"ok": "V redu, sem si zapisal!",
"ok_lnd": "V redu, sem shranil.",
"text": "Prosimo zapišite si seznam besed (mnemonično seme) na list papirja. To je varnostna kopija, ki jo lahko uporabite za obnovitev denarnice na drugi napravi.",
"text_lnd": "Vzemite si trenutek, da shranite LNDHub avtentikacijske podatke. To je varnostna kopija, s katero lahko denarnico obnovite v drugi napravi.",
"title": "Vaša denarnica je ustvarjena..."
},
"receive": {
"details_create": "Ustvari",
"details_label": "Opis",
"details_setAmount": "Prejmi znesek",
"details_share": "deli",
"header": "Prejmi"
},
"send": {
"broadcastButton": "Objavi v omrežju",
"broadcastError": "napaka",
"broadcastNone": "Vnesite zgoščeno vrednost transakcije",
"broadcastPending": "v teku",
"broadcastSuccess": "uspešno",
"confirm_header": "Potrditev",
"confirm_sendNow": "Pošlji",
"create_amount": "Znesek",
"create_broadcast": "Objavi v omrežju",
"create_copy": "Kopiraj in objavi kasneje",
"create_details": "Podrobnosti",
"create_fee": "Omrežnina",
"create_memo": "Opomba",
"create_satoshi_per_byte": "Satoshi na bajt",
"create_this_is_hex": "To je šestnajstiška vrednost transakcije, podpisana in pripravljena za objavo v omrežju.",
"create_to": "Za",
"create_tx_size": "Velikost transakcije",
"create_verify": "Preverite na coinb.in",
"details_add_rec_add": "Dodaj prejemnika",
"details_add_rec_rem": "Odstrani prejemnika",
"details_address": "naslov",
"details_address_field_is_not_valid": "Naslov ni veljaven",
"details_adv_fee_bump": "Omogoči Povečanje Omrežnine",
"details_adv_full": "Uporabi Celotno Stanje",
"details_adv_full_remove": "Drugi prejemniki bodo odstranjeni iz te transakcije.",
"details_adv_full_sure": "Ali ste prepričani, da želite za to transakcijo uporabiti celotno stanje denarnice?",
"details_adv_import": "Uvozi transakcijo",
"details_amount_field_is_not_valid": "Znesek ni veljaven",
"details_create": "Ustvari Račun",
"details_error_decode": "Napaka: Ni mogoče dekodirati Bitcoin naslova",
"details_fee_field_is_not_valid": "Omrežnina ni veljavna",
"details_next": "Naprej",
"details_no_maximum": "Izbrana denarnica ne podpira samodejnega izračuna največjega stanja. Ali ste prepričani, da želite izbrati to denarnico?",
"details_no_multiple": "Izbrana denarnica ne podpira pošiljanja več prejemnikom. Ali ste prepričani, da želite izbrati to denarnico?",
"details_no_signed_tx": "Izbrana datoteka ne vsebuje podpisane transakcije, ki jo je mogoče uvoziti.",
"details_note_placeholder": "lastna opomba",
"details_scan": "Skeniraj",
"details_total_exceeds_balance": "Znesek presega razpoložljivo stanje.",
"details_wallet_before_tx": "Preden ustvarite transakcijo, morate dodati Bitcoin denarnico.",
"details_wallet_selection": "Izbira Denarnice",
"dynamic_init": "Inicializacija",
"dynamic_next": "Naprej",
"dynamic_prev": "Nazaj",
"dynamic_start": "Začni",
"dynamic_stop": "Ustavi",
"header": "Pošlji",
"input_clear": "Počisti",
"input_done": "Končano",
"input_paste": "Prilepi",
"input_total": "Skupaj:",
"permission_camera_message": "Za uporabo kamere potrebujemo dovoljenje",
"permission_camera_title": "Dovoljenje za uporabo kamere",
"open_settings": "Odpri Nastavitve",
"permission_storage_later": "Vprašaj me kasneje",
"permission_storage_message": "BlueWallet potrebuje dovoljenje za dostop do vaše shrambe, da shrani to transakcijo.",
"permission_storage_title": "BlueWallet dovoljenje za dostop do shrambe",
"psbt_clipboard": "Kopiraj v odložišče",
"psbt_this_is_psbt": "To je delno podpisana bitcoin transakcija (PSBT). Dokončajte podpis s svojo strojno denarnico.",
"psbt_tx_export": "Izvozi v datoteko",
"psbt_tx_open": "Odpri podpisano transakcijo",
"psbt_tx_scan": "Skeniraj podpisano transakcijo",
"qr_error_no_qrcode": "Izbrana slika ne vsebuje QR kode.",
"qr_error_no_wallet": "Izbrana datoteka ne vsebuje denarnice, ki jo je mogoče uvoziti.",
"success_done": "Končano",
"txSaved": "Transakcijska datoteka ({filePath}) je bila shranjena v mapo Prenosi."
},
"settings": {
"about": "O aplikaciji",
"about_awesome": "Zgrajeno z izjemnimi",
"about_backup": "Vedno varnostno kopirajte svoje ključe!",
"about_free": "BlueWallet je brezplačen in odprtokodni projekt. Izdelali uporabniki Bitcoina.",
"about_release_notes": "Opombe ob izdaji",
"about_review": "Ocenite, napišite mnenje",
"about_selftest": "Zaženi samotestiranje",
"about_sm_github": "GitHub",
"about_sm_telegram": "Telegram klepet",
"about_sm_twitter": "Sledite nam na Twitterju",
"advanced_options": "Napredne Možnosti",
"currency": "Valuta",
"currency_source": "Cene so pridobljene iz CoinDesk",
"default_desc": "Ko je onemogočeno, bo BlueWallet ob zagonu takoj odprl izbrano denarnico.",
"default_info": "Privzeto v",
"default_title": "Ob zagonu",
"default_wallets": "Prikaži vse denarnice",
"electrum_connected": "Povezano",
"electrum_connected_not": "Brez povezave",
"electrum_error_connect": "Povezave s podanim Electrum strežnikom ni mogoče vzpostaviti",
"electrum_host": "gostitelj, na primer {example}",
"electrum_port": "TCP vrata, ponavadi {example}",
"electrum_port_ssl": "SSL vrata, ponavadi {example}",
"electrum_saved": "Spremembe so bile uspešno shranjene. Da bodo spremembe začele veljati, bo morda potreben ponovni zagon.",
"electrum_settings": "Electrum Nastavitve",
"electrum_settings_explain": "Pustite prazno za uporabo privzetih nastavitev",
"electrum_status": "Stanje",
"encrypt_decrypt": "Dešifriraj Shrambo",
"encrypt_decrypt_q": "Ali ste prepričani, da želite dešifrirati shrambo? To bo omogočilo dostop do vaših denarnic brez gesla.",
"encrypt_del_uninstall": "Izbriši, če je BlueWallet odstranjen",
"encrypt_enc_and_pass": "Šifrirano in Zaščiteno z geslom",
"encrypt_title": "Varnost",
"encrypt_tstorage": "shramba",
"encrypt_use": "Uporabi {type}",
"encrypt_use_expl": "{type} bo uporabljen za potrditev vaše identitete pred izvedbo transakcije, odklepanjem, izvozom ali brisanjem denarnice. {type} ne bo uporabljen za odklepanje šifrirane shrambe.",
"general": "Splošno",
"general_adv_mode": "Napredni način",
"general_adv_mode_e": "Ko je omogočen, boste videli napredne možnosti, kot so različni tipi denarnic, možnost določitve LNDHub strežnika, s katerim se želite povezati in entropija po meri pri ustvarjanju denarnice.",
"general_continuity": "Neprekinjenost",
"general_continuity_e": "Ko je omogočeno, si boste lahko ogledali izbrane denarnice in transakcije z uporabo vaših drugih naprav povezanih z Apple iCloud.",
"header": "nastavitve",
"language": "Jezik",
"language_restart": "Pri izbiri novega jezika bo morda potrebno ponovno zagnati BlueWallet, da bo sprememba začela veljati.",
"lightning_error_lndhub_uri": "Neveljaven LndHub URI",
"lightning_saved": "Spremembe so bile uspešno shranjene",
"lightning_settings": "Lightning Nastavitve",
"lightning_settings_explain": "Za povezavo z lastnim LND vozliščem, prosimo namestite LndHub in vnesite URL vozlišča v nastavitve. Pustite prazno za uporabo BlueWallet LNDHub (lndhub.io). Denarnice ustvarjene po potrditvi sprememb bodo povezane z novim LNDHub-om.",
"network": "Omrežje",
"network_broadcast": "Objavi transakcijo",
"network_electrum": "Electrum strežnik",
"password": "Geslo",
"password_explain": "Ustvarite geslo za dešifriranje shrambe",
"passwords_do_not_match": "Gesli se ne ujemata",
"plausible_deniability": "Verodostojno zanikanje",
"retype_password": "Ponovno vpišite geslo",
"notifications": "Obvestila",
"save": "Shrani",
"saved": "Shranjeno",
"not_a_valid_uri": "Neveljaven URI",
"push_notifications": "Potisna obvestila",
"groundcontrol_explanation": "GroundControl je brezplačen odprtokoden strežnik potisnih obvestil za bitcoin denarnice. Da se ne zanašate na BlueWallet infrastrukturo, lahko namestite svoj strežnik GroundControl in tukaj dodate njegov URL. Pustite prazno, za uporabo privzetega"
},
"transactions": {
"cancel_explain": "To transakcijo bomo nadomestili z novo, ki plača višjo omrežnino. To prekliče prejšnjo transakcijo. To se imenuje RBF - Replace By Fee.",
"cancel_no": "Ta transakcija ni zamenljiva",
"cancel_title": "Prekliči to transakcijo (RBF)",
"cpfp_create": "Ustvari",
"cpfp_exp": "Ustvarjena bo nova transakcija, ki porabi vašo nepotrjeno transakcijo. Skupna omrežnina bo višja od omrežnine prvotne transakcije, zato bi morala biti potrditev hitrejša. To se imenuje CPFP - Child Pays For Parent.",
"cpfp_no_bump": "Tej transakciji ni mogoče povečati omrežnine",
"cpfp_title": "Povečaj omrežnino (CPFP)",
"details_block": "Višina Bloka",
"details_copy": "Kopiraj",
"details_from": "Vhod",
"details_inputs": "Vhodi",
"details_outputs": "Izhodi",
"details_received": "Prejeto",
"details_show_in_block_explorer": "Prikaži v raziskovalcu blokov",
"details_title": "Transakcija",
"details_to": "Izhod",
"details_transaction_details": "Podrobnosti transakcije",
"enable_hw": "Ta denarnica se ne uporablja skupaj s strojno denarnico. Ali želite omogočiti uporabo strojne denarnice?",
"list_conf": "potrd",
"list_title": "transakcije",
"transactions_count": "število transakcij",
"rbf_explain": "To transakcijo bomo nadomestili z novo, ki plača višjo omrežnino, zato bi morala biti potrditev hitrejša. To se imenuje RBF - Replace By Fee.",
"rbf_title": "Povečaj omrežnino (RBF)",
"status_bump": "Povečaj omrežnino",
"status_cancel": "Prekliči transakcijo"
},
"wallets": {
"add_bitcoin": "Bitcoin",
"add_create": "Ustvari",
"add_entropy_generated": "{gen} bajtov ustvarjene entropije",
"add_entropy_provide": "Zagotovite entropijo s pomočjo metov kocke",
"add_entropy_remain": "{gen} bajtov ustvarjene entropije. Preostalih {rem} bajtov bo pridobljenih iz sistemskega generatorja naključnih števil.",
"add_import_wallet": "Uvozi denarnico",
"import_file": "Uvozi datoteko",
"add_lightning": "Lightning",
"add_lndhub": "Povežite se s svojim LNDHub-om",
"add_lndhub_error": "Podan naslov vozlišča ni veljavno vozlišče LNDHub.",
"add_lndhub_placeholder": "naslov vašega vozlišča",
"add_or": "ali",
"add_title": "Dodajte denarnico",
"add_wallet_name": "ime",
"add_wallet_type": "tip",
"details_address": "Naslov",
"details_advanced": "Napredno",
"details_are_you_sure": "Ali ste prepričani?",
"details_connected_to": "Povezano z",
"details_del_wb": "Stanje",
"details_del_wb_err": "Navedeni znesek stanja se ne ujema s stanjem v tej denarnici. Prosimo poskusite ponovno",
"details_del_wb_q": "Ta denarnica ima pozitivno stanje. Preden nadaljujete, se zavedajte, da sredstev ne boste mogli obnoviti brez seznama besed te denarnice (mnemonično seme). Da se izognete nenamerni odstranitvi te denarnice, vnesite stanje svoje denarnice {balance} satošijev.",
"details_delete": "Izbriši",
"details_delete_wallet": "Izbriši denarnico",
"details_display": "prikaži na seznamu denarnic",
"details_export_backup": "Izvozi / varnostna kopija",
"details_marketplace": "Tržnica",
"details_master_fingerprint": "Master fingerprint",
"details_no_cancel": "Ne, prekliči",
"details_save": "Shrani",
"details_show_xpub": "Prikaži XPUB denarnice",
"details_title": "Denarnica",
"details_type": "Tip",
"details_use_with_hardware_wallet": "Uporaba s strojno denarnico",
"details_wallet_updated": "Denarnica posodobljena",
"details_yes_delete": "Da, izbriši",
"export_title": "izvoz denarnice",
"import_do_import": "Uvozi",
"import_error": "Napaka pri uvozu. Prosimo, preverite ali so podani podatki pravilni.",
"import_explanation": "Tu napišite vaš seznam besed, zasebni ključ, WIF, ali karkoli drugega imate. BlueWallet se bo potrudil uganiti pravilno obliko in uvoziti vašo denarnico",
"import_imported": "Uvoženo",
"import_scan_qr": "Skenirajte ali uvozite datoteko",
"import_success": "Vaša denarnica je bila uspešno uvožena.",
"import_title": "uvozi",
"list_create_a_button": "Ustvarite",
"list_create_a_wallet": "Ustvarite denarnico",
"list_create_a_wallet1": "Je brezplačno in lahko jih ustvarite",
"list_create_a_wallet2": "toliko, kot želite",
"list_empty_txs1": "Tu bodo prikazane vaše transakcije",
"list_empty_txs1_lightning": "Lightning denarnica je namenjena za vsakodnevne transakcije. Provizije so nepošteno poceni in hitrost je izjemno hitra.",
"list_empty_txs2": "Začnite uporabljati denarnico",
"list_empty_txs2_lightning": "\nČe želite začeti z uporabo, tapnite na \"upravljanje sredstev\" in napolnite denarnico.",
"list_header": "Denarnica predstavlja par ključev, enega zasebnega (skrivnost) in enega (naslov), ki ga lahko delite za prejemanje kovancev.",
"list_import_error": "Pri poskusu uvoza te denarnice je prišlo do napake.",
"list_import_problem": "Pri uvozu te denarnice je prišlo do težave",
"list_latest_transaction": "zadnja transakcija",
"list_long_choose": "Izberite fotografijo",
"list_long_clipboard": "Kopiraj iz odložišča",
"list_long_scan": "Skeniraj QR kodo",
"take_photo": "Fotografiraj",
"list_tap_here_to_buy": "Kupite Bitcoin",
"list_title": "denarnice",
"list_tryagain": "Poskusi ponovno",
"reorder_title": "Preureditev Denarnic",
"select_no_bitcoin": "Trenutno ni na voljo nobena Bitcoin denarnica.",
"select_no_bitcoin_exp": "Za napolnitev Lightning denarnic je potrebna Bitcoin denarnica. Ustvarite ali uvozite denarnico.",
"select_wallet": "Izberite Denarnico",
"xpub_copiedToClipboard": "Kopirano v odložišče.",
"xpub_title": "XPUB denarnice"
}
}

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' },

99
package-lock.json generated
View file

@ -1,6 +1,6 @@
{
"name": "bluewallet",
"version": "5.5.7",
"version": "5.5.8",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@ -4244,9 +4244,9 @@
}
},
"@react-native-community/async-storage": {
"version": "1.11.0",
"resolved": "https://registry.npmjs.org/@react-native-community/async-storage/-/async-storage-1.11.0.tgz",
"integrity": "sha512-Pq9LlmvtCEKAGdkyrgTcRxNh2fnHFykEj2qnRYijOl1pDIl2MkD5IxaXu5eOL0wgOtAl4U//ff4z40Td6XR5rw==",
"version": "1.12.0",
"resolved": "https://registry.npmjs.org/@react-native-community/async-storage/-/async-storage-1.12.0.tgz",
"integrity": "sha512-y3zVxuVyiOxI8TXrvajmYfDbIt2vFNxzV5MiA28v15DQTxDk6uJH3rpc9my+la7u2Tiwt3PpdU2+59ZgZ4h7wA==",
"requires": {
"deep-assign": "^3.0.0"
}
@ -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",
@ -4929,18 +4938,18 @@
"integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw=="
},
"@types/react": {
"version": "16.9.46",
"resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.46.tgz",
"integrity": "sha512-dbHzO3aAq1lB3jRQuNpuZ/mnu+CdD3H0WVaaBQA8LTT3S33xhVBUj232T8M3tAhSWJs/D/UqORYUlJNl/8VQZg==",
"version": "16.9.49",
"resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.49.tgz",
"integrity": "sha512-DtLFjSj0OYAdVLBbyjhuV9CdGVHCkHn2R+xr3XkBvK2rS1Y1tkc14XSGjYgm5Fjjr90AxH9tiSzc1pCFMGO06g==",
"requires": {
"@types/prop-types": "*",
"csstype": "^3.0.2"
}
},
"@types/react-native": {
"version": "0.63.9",
"resolved": "https://registry.npmjs.org/@types/react-native/-/react-native-0.63.9.tgz",
"integrity": "sha512-6ec/z9zjAkFH3rD1RYqbrA/Lj+jux6bumWCte4yRy3leyelTdqtmOd2Ph+86IXQQzsIArEMBwmraAbNQ0J3UAA==",
"version": "0.63.13",
"resolved": "https://registry.npmjs.org/@types/react-native/-/react-native-0.63.13.tgz",
"integrity": "sha512-diqOQUlMB4+l5tldIP38fTkjtPn6pnFMMLfewiMtpUYwB1ID7snQ/ePN98a+3BFG87v8H62Rp/Q1xuudvG4MSg==",
"requires": {
"@types/react": "*"
}
@ -7281,9 +7290,9 @@
}
},
"csstype": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.2.tgz",
"integrity": "sha512-ofovWglpqoqbfLNOTBNZLSbMuGrblAf1efvvArGKOZMBrIoJeu5UsAipQolkijtyQx5MtAzT/J9IHj/CEY1mJw=="
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.3.tgz",
"integrity": "sha512-jPl+wbWPOWJ7SXsWyqGRk3lGecbar0Cb0OvZF/r/ZU011R4YqiRehgkQ9p4eQfo9DSDLqLL3wHwfxeJiuIsNag=="
},
"dashdash": {
"version": "1.14.1",
@ -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",
@ -13061,9 +13070,9 @@
}
},
"metro-react-native-babel-preset": {
"version": "0.61.0",
"resolved": "https://registry.npmjs.org/metro-react-native-babel-preset/-/metro-react-native-babel-preset-0.61.0.tgz",
"integrity": "sha512-k0j2K70YadKFFayFOtw9sbaB38LdkkJLluwqHvyl9CRAa3m7cWQ6pZbakCPrp3OWyo7dJWbP70ybOvjoDv2jwQ==",
"version": "0.62.0",
"resolved": "https://registry.npmjs.org/metro-react-native-babel-preset/-/metro-react-native-babel-preset-0.62.0.tgz",
"integrity": "sha512-TznN3fyzo68A7TsO5paUsvKY4bXEk0BreVVXY/hKUXxv8p0Ts7gOB9ciXX2QT+8Egcs6+T4z/S06bONUYlWFJg==",
"requires": {
"@babel/core": "^7.0.0",
"@babel/plugin-proposal-class-properties": "^7.0.0",
@ -13381,9 +13390,9 @@
"optional": true
},
"needle": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/needle/-/needle-2.5.0.tgz",
"integrity": "sha512-o/qITSDR0JCyCKEQ1/1bnUXMmznxabbwi/Y4WwJElf+evwJNFNwIDMCCt5IigFVxgeGBJESLohGtIS9gEzo1fA==",
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/needle/-/needle-2.5.2.tgz",
"integrity": "sha512-LbRIwS9BfkPvNwNHlsA41Q29kL2L/6VaOJ0qisM5lLWsTV3nP15abO5ITL6L81zqFhzjRKDAYjpcBcwM0AVvLQ==",
"requires": {
"debug": "^3.2.6",
"iconv-lite": "^0.4.4",
@ -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"
}
@ -14993,9 +15002,9 @@
"from": "git+https://github.com/BlueWallet/react-native-document-picker.git#3684d4fcc2bc0b47c32be39024e4796004c3e428"
},
"react-native-elements": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/react-native-elements/-/react-native-elements-2.1.0.tgz",
"integrity": "sha512-X487S6794bwQm3HPyYKuNB7cDhZWwmUKiPdPiacVas3AYqKWIEcPuGpVt6+JzSsnV6xWQe9c5lxST8y6e31kcg==",
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/react-native-elements/-/react-native-elements-2.2.1.tgz",
"integrity": "sha512-JhveP4ZQzZrMef9sBJfjfgfdbKafGnqZGosVr4hb3At8c1wJ3QFgOGUsKxhpjpF2wr/RgLbP2UV3rS1cD+t7IA==",
"requires": {
"@types/react-native-vector-icons": "^6.4.5",
"color": "^3.1.0",
@ -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",
@ -15358,9 +15383,9 @@
}
},
"react-native-watch-connectivity": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/react-native-watch-connectivity/-/react-native-watch-connectivity-0.5.0.tgz",
"integrity": "sha512-M6JtF3prEdFAGDcauTLCM6RIvh4xy8A/j+6u3sNsTw+BoS9rmshs6zdyaB0FdACQrBXIH5LgV10OoIN1UFsVKw==",
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/react-native-watch-connectivity/-/react-native-watch-connectivity-1.0.2.tgz",
"integrity": "sha512-uNx6Zhx++LpRRyQddukBcMyMtWnleP97OME9HEkW91rEuwTviBK12+1sn/YGBgGLrmdEULAoPO91w619w2Efbw==",
"requires": {
"lodash.sortby": "^4.7.0"
}

View file

@ -1,6 +1,6 @@
{
"name": "bluewallet",
"version": "5.5.7",
"version": "5.5.8",
"license": "MIT",
"devDependencies": {
"@babel/core": "^7.9.6",
@ -61,13 +61,14 @@
},
"dependencies": {
"@babel/preset-env": "7.11.0",
"@react-native-community/async-storage": "1.11.0",
"@react-native-community/async-storage": "1.12.0",
"@react-native-community/blur": "3.6.0",
"@react-native-community/clipboard": "1.2.3",
"@react-native-community/geolocation": "2.0.2",
"@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",
@ -101,7 +102,7 @@
"frisbee": "3.1.4",
"lottie-ios": "3.1.8",
"lottie-react-native": "3.5.0",
"metro-react-native-babel-preset": "0.61.0",
"metro-react-native-babel-preset": "0.62.0",
"path-browserify": "1.0.1",
"pbkdf2": "3.1.1",
"prettier": "2.0.5",
@ -112,16 +113,17 @@
"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",
"react-native-elements": "2.1.0",
"react-native-elements": "2.2.1",
"react-native-fs": "2.16.6",
"react-native-gesture-handler": "1.7.0",
"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",
@ -146,7 +149,7 @@
"react-native-tcp-socket": "3.7.1",
"react-native-tooltip": "git+https://github.com/BlueWallet/react-native-tooltip.git#d369e7ece09e4dec73873f1cfeac83e9d35294a6",
"react-native-vector-icons": "6.6.0",
"react-native-watch-connectivity": "0.5.0",
"react-native-watch-connectivity": "1.0.2",
"react-native-webview": "10.8.3",
"react-test-render": "1.1.2",
"readable-stream": "3.6.0",

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>
) : (
<View style={styles.payButtonContainer}>
<BlueButton title={loc.lnd.payButton} onPress={() => this.pay()} disabled={this.shouldDisablePayButton()} />
</View>
)}
</BlueCard>
</BlueCard>

View file

@ -1,6 +1,6 @@
/* global alert */
import React, { useState } from 'react';
import { Image, View, TouchableOpacity, StatusBar, Platform, StyleSheet, Linking } from 'react-native';
import { Image, View, TouchableOpacity, StatusBar, Platform, StyleSheet, Linking, Alert } from 'react-native';
import { RNCamera } from 'react-native-camera';
import { Icon } from 'react-native-elements';
import ImagePicker from 'react-native-image-picker';
@ -9,8 +9,10 @@ import DocumentPicker from 'react-native-document-picker';
import RNFS from 'react-native-fs';
import loc from '../../loc';
import { BlueLoadingHook, BlueTextHooks, BlueButtonHook, BlueSpacing40 } from '../../BlueComponents';
import { getSystemName } from 'react-native-device-info';
const LocalQRCode = require('@remobile/react-native-qrcode-local-image');
const createHash = require('create-hash');
const isDesktop = getSystemName() === 'Mac OS X';
const styles = StyleSheet.create({
root: {
@ -169,10 +171,6 @@ const ScanQRCode = () => {
setCameraStatus(event.cameraStatus);
};
const handleOpenSettingsTapped = () => {
Linking.openSettings();
};
return isLoading ? (
<View style={styles.root}>
<BlueLoadingHook />
@ -199,7 +197,7 @@ const ScanQRCode = () => {
<View style={[styles.openSettingsContainer, stylesHook.openSettingsContainer]}>
<BlueTextHooks>{loc.send.permission_camera_message}</BlueTextHooks>
<BlueSpacing40 />
<BlueButtonHook title={loc.send.open_settings} onPress={handleOpenSettingsTapped} />
<BlueButtonHook title={loc.send.open_settings} onPress={ScanQRCode.openPrivacyDesktopSettings} />
</View>
)}
<TouchableOpacity style={styles.closeTouch} onPress={dismiss}>
@ -217,6 +215,34 @@ const ScanQRCode = () => {
);
};
ScanQRCode.openPrivacyDesktopSettings = () => {
if (isDesktop) {
Linking.openURL('x-apple.systempreferences:com.apple.preference.security?Privacy_Camera');
} else {
Linking.openSettings();
}
};
ScanQRCode.presentCameraNotAuthorizedAlert = error => {
Alert.alert(
loc.errors.error,
error,
[
{
text: loc.send.open_settings,
onPress: ScanQRCode.openPrivacyDesktopSettings,
style: 'default',
},
{
text: loc._.ok,
onPress: () => {},
style: 'cancel',
},
],
{ cancelable: true },
);
};
ScanQRCode.navigationOptions = {
headerShown: false,
};

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

@ -25,7 +25,9 @@ import Privacy from '../../Privacy';
import { BitcoinUnit } from '../../models/bitcoinUnits';
import loc from '../../loc';
import { BlueCurrentTheme } from '../../components/themes';
import { getSystemName } from 'react-native-device-info';
const currency = require('../../blue_modules/currency');
const isDesktop = getSystemName() === 'Mac OS X';
export default class SendCreate extends Component {
constructor(props) {
@ -57,8 +59,12 @@ export default class SendCreate extends Component {
await RNFS.writeFile(filePath, this.state.tx);
Share.open({
url: 'file://' + filePath,
saveToFiles: isDesktop,
})
.catch(error => {
console.log(error);
alert(error.message);
})
.catch(error => console.log(error))
.finally(() => {
RNFS.unlink(filePath);
});

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

@ -36,6 +36,7 @@ import DocumentPicker from 'react-native-document-picker';
import { decodeUR, extractSingleWorkload } from 'bc-ur/dist';
import loc from '../../loc';
import { BlueCurrentTheme } from '../../components/themes';
import ScanQRCode from './ScanQRCode';
const EV = require('../../blue_modules/events');
const BlueElectrum = require('../../blue_modules/BlueElectrum');
/** @type {AppStorage} */
@ -290,8 +291,12 @@ export default class PsbtWithHardwareWallet extends Component {
await RNFS.writeFile(filePath, typeof this.state.psbt === 'string' ? this.state.psbt : this.state.psbt.toBase64());
Share.open({
url: 'file://' + filePath,
saveToFiles: isDesktop,
})
.catch(error => {
console.log(error);
alert(error.message);
})
.catch(error => console.log(error))
.finally(() => {
RNFS.unlink(filePath);
});
@ -352,6 +357,8 @@ export default class PsbtWithHardwareWallet extends Component {
alert(loc.send.qr_error_no_qrcode);
}
});
} else if (response.error) {
ScanQRCode.presentCameraNotAuthorizedAlert(response.error);
}
},
);

View file

@ -205,7 +205,7 @@ const WalletsAdd = () => {
}
BlueApp.wallets.push(w);
await BlueApp.saveToDisk();
EV(EV.enum.WALLETS_COUNT_CHANGED);
setTimeout(() => EV(EV.enum.WALLETS_COUNT_CHANGED), 500); // heavy task; hopefully will be executed while user is staring at backup screen
A(A.ENUM.CREATED_WALLET);
ReactNativeHapticFeedback.trigger('notificationSuccess', { ignoreAndroidSystemSettings: false });
if (w.type === HDSegwitP2SHWallet.type || w.type === HDSegwitBech32Wallet.type) {

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;
}
if (getSystemName() === 'Mac OS X') {
Linking.openURL(uri).finally(() => this.props.navigation.goBack(null));
} else {
this.setState({ uri, isLoading: false, address });
return uri;
}
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

@ -23,6 +23,7 @@ import loc from '../../loc';
import { getSystemName } from 'react-native-device-info';
import RNFS from 'react-native-fs';
import DocumentPicker from 'react-native-document-picker';
import ScanQRCode from '../send/ScanQRCode';
const LocalQRCode = require('@remobile/react-native-qrcode-local-image');
const { width } = Dimensions.get('window');
const isDesktop = getSystemName() === 'Mac OS X';
@ -147,6 +148,8 @@ const WalletsImport = () => {
alert(loc.send.qr_error_no_qrcode);
}
});
} else if (response.error) {
ScanQRCode.presentCameraNotAuthorizedAlert(response.error);
}
},
);

View file

@ -7,10 +7,10 @@ import {
Text,
StyleSheet,
InteractionManager,
Clipboard,
SectionList,
Alert,
Platform,
Dimensions,
} from 'react-native';
import { BlueScanButton, WalletsCarousel, BlueHeaderDefaultMain, BlueTransactionListItem, BlueNavigationStyle } from '../../BlueComponents';
import { Icon } from 'react-native-elements';
@ -21,10 +21,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 { 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');
const BlueApp: AppStorage = require('../../BlueApp');
@ -40,12 +42,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));
@ -60,28 +66,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);
}
@ -128,11 +115,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 {
@ -140,6 +122,7 @@ export default class WalletsList extends Component {
}
const wallets = BlueApp.getWallets().concat(false);
const dataSource = BlueApp.getTransactions(null, 10);
if (scrollToEnd) {
scrollToEnd = wallets.length > this.state.wallets.length;
}
@ -148,12 +131,13 @@ export default class WalletsList extends Component {
{
isLoading: false,
isFlatListRefreshControlHidden: true,
dataSource: BlueApp.getTransactions(null, 10),
wallets: BlueApp.getWallets().concat(false),
dataSource,
wallets,
},
() => {
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);
}
},
);
@ -342,6 +326,8 @@ export default class WalletsList extends Component {
onSnapToItem={this.onSnapToItem}
ref={this.walletsCarousel}
testID="WalletsList"
sliderWidth={Dimensions.get('window').width}
itemWidth={this.state.itemWidth}
/>
);
};
@ -349,7 +335,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:
@ -362,7 +348,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={
@ -478,6 +464,8 @@ export default class WalletsList extends Component {
alert(loc.send.qr_error_no_qrcode);
}
});
} else if (response.error) {
ScanQRCode.presentCameraNotAuthorizedAlert(response.error);
}
},
);
@ -552,9 +540,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

View file

@ -33,6 +33,7 @@ const SelectWallet = ({ navigation }) => {
justifyContent: 'center',
alignContent: 'center',
paddingTop: 20,
backgroundColor: colors.background,
},
itemRoot: {
backgroundColor: 'transparent',

View file

@ -1,5 +1,5 @@
/* global alert */
import React, { Component } from 'react';
import React, { useEffect, useState } from 'react';
import { Chain } from '../../models/bitcoinUnits';
import {
Text,
@ -8,43 +8,41 @@ import {
View,
Keyboard,
ActivityIndicator,
InteractionManager,
FlatList,
Dimensions,
ScrollView,
TouchableOpacity,
StatusBar,
Linking,
KeyboardAvoidingView,
Alert,
InteractionManager,
useWindowDimensions,
} from 'react-native';
import PropTypes from 'prop-types';
import ImagePicker from 'react-native-image-picker';
import Clipboard from '@react-native-community/clipboard';
import {
BlueSendButtonIcon,
BlueListItem,
BlueReceiveButtonIcon,
BlueTransactionListItem,
BlueWalletNavigationHeader,
BlueAlertWalletExportReminder,
BlueListItemHooks,
} from '../../BlueComponents';
import WalletGradient from '../../class/wallet-gradient';
import { Icon } from 'react-native-elements';
import { LightningCustodianWallet, WatchOnlyWallet } from '../../class';
import Modal from 'react-native-modal';
import * as NavigationService from '../../NavigationService';
import HandoffSettings from '../../class/handoff';
import Handoff from 'react-native-handoff';
import { BlueCurrentTheme } from '../../components/themes';
import ActionSheet from '../ActionSheet';
import loc from '../../loc';
import { getSystemName } from 'react-native-device-info';
import { useRoute, useNavigation, useTheme } from '@react-navigation/native';
import BuyBitcoin from './buyBitcoin';
const BlueApp = require('../../BlueApp');
const EV = require('../../blue_modules/events');
const BlueElectrum = require('../../blue_modules/BlueElectrum');
const LocalQRCode = require('@remobile/react-native-qrcode-local-image');
const windowHeight = Dimensions.get('window').height;
const isDesktop = getSystemName() === 'Mac OS X';
const styles = StyleSheet.create({
@ -69,7 +67,6 @@ const styles = StyleSheet.create({
height: 200,
},
advancedTransactionOptionsModalContent: {
backgroundColor: BlueCurrentTheme.colors.elevated,
padding: 22,
borderTopLeftRadius: 16,
borderTopRightRadius: 16,
@ -105,10 +102,8 @@ const styles = StyleSheet.create({
marginBottom: 8,
fontWeight: 'bold',
fontSize: 24,
color: BlueCurrentTheme.colors.foregroundColor,
},
marketplaceButton1: {
backgroundColor: BlueCurrentTheme.colors.lightButton,
borderRadius: 9,
minHeight: 49,
flex: 1,
@ -119,7 +114,6 @@ const styles = StyleSheet.create({
},
marketplaceButton2: {
marginLeft: 5,
backgroundColor: BlueCurrentTheme.colors.lightButton,
borderRadius: 9,
minHeight: 49,
flex: 1,
@ -129,16 +123,13 @@ const styles = StyleSheet.create({
alignItems: 'center',
},
marketpalceText1: {
color: BlueCurrentTheme.colors.cta2,
fontSize: 18,
},
marketpalceText2: {
color: BlueCurrentTheme.colors.cta2,
fontSize: 18,
marginHorizontal: 8,
},
list: {
backgroundColor: BlueCurrentTheme.colors.background,
flex: 1,
},
emptyTxs: {
@ -183,50 +174,46 @@ const styles = StyleSheet.create({
},
});
export default class WalletTransactions extends Component {
walletBalanceText = null;
const WalletTransactions = () => {
const [isHandOffUseEnabled, setIsHandOffUseEnabled] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const [isManageFundsModalVisible, setIsManageFundsModalVisible] = useState(false);
const { wallet } = useRoute().params;
const name = useRoute().name;
const [itemPriceUnit, setItemPriceUnit] = useState(wallet.getPreferredBalanceUnit());
const [dataSource, setDataSource] = useState(wallet.getTransactions(15));
const [timeElapsed, setTimeElapsed] = useState(0);
const [limit, setLimit] = useState(15);
const [pageSize, setPageSize] = useState(20);
const { setParams, navigate } = useNavigation();
const { colors } = useTheme();
constructor(props) {
super(props);
// here, when we receive REMOTE_TRANSACTIONS_COUNT_CHANGED we fetch TXs and balance for current wallet
EV(EV.enum.REMOTE_TRANSACTIONS_COUNT_CHANGED, this.refreshTransactionsFunction.bind(this), true);
const wallet = props.route.params.wallet;
this.props.navigation.setParams({ wallet: wallet, isLoading: true });
this.state = {
isHandOffUseEnabled: false,
isLoading: true,
isManageFundsModalVisible: false,
showShowFlatListRefreshControl: false,
wallet: wallet,
itemPriceUnit: wallet.getPreferredBalanceUnit(),
dataSource: this.getTransactions(15),
timeElapsed: 0, // this is to force a re-render for FlatList items.
limit: 15,
pageSize: 20,
};
}
componentDidMount() {
this._unsubscribeFocus = this.props.navigation.addListener('focus', this.onFocus);
this.props.navigation.setParams({ isLoading: false });
this.interval = setInterval(() => {
this.setState(prev => ({ timeElapsed: prev.timeElapsed + 1 }));
}, 60000);
HandoffSettings.isHandoffUseEnabled().then(enabled => this.setState({ isHandOffUseEnabled: enabled }));
this.setState({ isLoading: false });
}
/**
* Forcefully fetches TXs and balance for wallet
*/
refreshTransactionsFunction(delay) {
delay = delay || 4000;
const that = this;
setTimeout(function () {
that.refreshTransactions();
}, delay); // giving a chance to remote server to propagate
}
const windowHeight = useWindowDimensions().height;
const windowWidth = useWindowDimensions().width;
const stylesHook = StyleSheet.create({
advancedTransactionOptionsModalContent: {
backgroundColor: colors.elevated,
},
listHeaderText: {
color: colors.foregroundColor,
},
marketplaceButton1: {
backgroundColor: colors.lightButton,
},
marketplaceButton2: {
backgroundColor: colors.lightButton,
},
marketpalceText1: {
color: colors.cta2,
},
marketpalceText2: {
color: colors.cta2,
},
list: {
backgroundColor: colors.background,
},
});
const interval = setInterval(() => setTimeElapsed(prev => ({ timeElapsed: prev.timeElapsed + 1 })), 60000);
/**
* Simple wrapper for `wallet.getTransactions()`, where `wallet` is current wallet.
@ -235,9 +222,7 @@ export default class WalletTransactions extends Component {
* @param limit {Integer} How many txs return, starting from the earliest. Default: all of them.
* @returns {Array}
*/
getTransactions(limit = Infinity) {
const wallet = this.props.route.params.wallet;
const getTransactions = (limit = Infinity) => {
let txs = wallet.getTransactions();
for (const tx of txs) {
tx.sort_ts = +new Date(tx.received);
@ -246,47 +231,63 @@ export default class WalletTransactions extends Component {
return b.sort_ts - a.sort_ts;
});
return txs.slice(0, limit);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
};
redrawScreen() {
InteractionManager.runAfterInteractions(async () => {
console.log('wallets/transactions redrawScreen()');
useEffect(() => {
EV(EV.enum.REMOTE_TRANSACTIONS_COUNT_CHANGED, refreshTransactionsFunction, true);
HandoffSettings.isHandoffUseEnabled().then(setIsHandOffUseEnabled);
return () => {
clearInterval(interval);
navigate('DrawerRoot', { selectedWallet: '' });
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
this.setState({
isLoading: false,
showShowFlatListRefreshControl: false,
dataSource: this.getTransactions(this.state.limit),
});
});
}
useEffect(() => {
setIsLoading(true);
setLimit(15);
setPageSize(20);
setTimeElapsed(0);
setDataSource(wallet.getTransactions(15));
setItemPriceUnit(wallet.getPreferredBalanceUnit());
setParams({ wallet, isLoading: false });
setIsLoading(false);
navigate('DrawerRoot', { selectedWallet: wallet.getID() });
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [wallet]);
isLightning() {
const w = this.state.wallet;
/**
* Forcefully fetches TXs and balance for wallet
*/
const refreshTransactionsFunction = delay => {
delay = delay || 4000;
setTimeout(function () {
refreshTransactions();
}, delay); // giving a chance to remote server to propagate
};
const isLightning = () => {
const w = wallet;
if (w && w.chain === Chain.OFFCHAIN) {
return true;
}
return false;
}
};
/**
* Forcefully fetches TXs and balance for wallet
*/
refreshTransactions() {
if (this.state.isLoading) return;
this.setState(
{
showShowFlatListRefreshControl: true,
isLoading: true,
},
async () => {
const refreshTransactions = async () => {
if (isLoading) return;
setIsLoading(true);
let noErr = true;
let smthChanged = false;
try {
// await BlueElectrum.ping();
await BlueElectrum.waitTillConnected();
/** @type {LegacyWallet} */
const wallet = this.state.wallet;
const balanceStart = +new Date();
const oldBalance = wallet.getBalance();
await wallet.fetchBalance();
@ -308,30 +309,26 @@ export default class WalletTransactions extends Component {
} catch (err) {
noErr = false;
alert(err.message);
this.setState({
isLoading: false,
showShowFlatListRefreshControl: false,
});
setIsLoading(false);
}
if (noErr && smthChanged) {
console.log('saving to disk');
await BlueApp.saveToDisk(); // caching
EV(EV.enum.TRANSACTIONS_COUNT_CHANGED); // let other components know they should redraw
setDataSource(getTransactions(limit));
}
this.redrawScreen();
},
);
}
_keyExtractor = (_item, index) => index.toString();
renderListFooterComponent = () => {
// if not all txs rendered - display indicator
return (this.getTransactions(Infinity).length > this.state.limit && <ActivityIndicator style={styles.activityIndicator} />) || <View />;
setIsLoading(false);
};
renderListHeaderComponent = () => {
const style = { opacity: this.state.isLoading ? 0.5 : 1.0 };
const _keyExtractor = (_item, index) => index.toString();
const renderListFooterComponent = () => {
// if not all txs rendered - display indicator
return (getTransactions(Infinity).length > limit && <ActivityIndicator style={styles.activityIndicator} />) || <View />;
};
const renderListHeaderComponent = () => {
const style = { opacity: isLoading ? 0.5 : 1.0 };
return (
<View style={styles.flex}>
<View style={styles.listHeader}>
@ -349,17 +346,15 @@ export default class WalletTransactions extends Component {
The idea is to avoid showing on iOS an appstore/market style app that goes against the TOS.
*/}
{this.state.wallet.getTransactions().length > 0 &&
this.state.wallet.type !== LightningCustodianWallet.type &&
this.renderSellFiat()}
{this.state.wallet.type === LightningCustodianWallet.type && this.renderMarketplaceButton()}
{this.state.wallet.type === LightningCustodianWallet.type && Platform.OS === 'ios' && this.renderLappBrowserButton()}
{wallet.getTransactions().length > 0 && wallet.type !== LightningCustodianWallet.type && renderSellFiat()}
{wallet.type === LightningCustodianWallet.type && renderMarketplaceButton()}
{wallet.type === LightningCustodianWallet.type && Platform.OS === 'ios' && renderLappBrowserButton()}
</View>
<View style={styles.listHeaderTextRow}>
<Text style={styles.listHeaderText}>{loc.transactions.list_title}</Text>
<View style={[styles.listHeaderTextRow, stylesHook.listHeaderTextRow]}>
<Text style={[styles.listHeaderText, stylesHook.listHeaderText]}>{loc.transactions.list_title}</Text>
{isDesktop && (
<TouchableOpacity style={style} onPress={() => this.refreshTransactions()} disabled={this.state.isLoading}>
<Icon name="refresh" type="font-awesome" color={BlueCurrentTheme.colors.feeText} />
<TouchableOpacity style={style} onPress={refreshTransactions} disabled={isLoading}>
<Icon name="refresh" type="font-awesome" color={colors.feeText} />
</TouchableOpacity>
)}
</View>
@ -367,65 +362,63 @@ export default class WalletTransactions extends Component {
);
};
renderManageFundsModal = () => {
const renderManageFundsModal = () => {
return (
<Modal
deviceHeight={windowHeight}
isVisible={this.state.isManageFundsModalVisible}
deviceWidth={windowWidth}
isVisible={isManageFundsModalVisible}
style={styles.bottomModal}
onBackdropPress={() => {
Keyboard.dismiss();
this.setState({ isManageFundsModalVisible: false });
setIsManageFundsModalVisible(false);
}}
>
<KeyboardAvoidingView behavior={Platform.OS === 'ios' ? 'position' : null}>
<View style={styles.advancedTransactionOptionsModalContent}>
<BlueListItem
<View style={[styles.advancedTransactionOptionsModalContent, stylesHook.advancedTransactionOptionsModalContent]}>
<BlueListItemHooks
hideChevron
component={TouchableOpacity}
onPress={a => {
onPress={() => {
const wallets = [...BlueApp.getWallets().filter(item => item.chain === Chain.ONCHAIN && item.allowSend())];
if (wallets.length === 0) {
alert(loc.lnd.refill_create);
} else {
this.setState({ isManageFundsModalVisible: false });
this.props.navigation.navigate('SelectWallet', { onWalletSelect: this.onWalletSelect, chainType: Chain.ONCHAIN });
setIsManageFundsModalVisible(false);
navigate('SelectWallet', { onWalletSelect, chainType: Chain.ONCHAIN });
}
}}
title={loc.lnd.refill}
/>
<BlueListItem
<BlueListItemHooks
hideChevron
component={TouchableOpacity}
onPress={a => {
this.setState({ isManageFundsModalVisible: false }, () =>
this.props.navigation.navigate('ReceiveDetails', {
secret: this.state.wallet.getSecret(),
}),
);
onPress={() => {
setIsManageFundsModalVisible(false);
navigate('ReceiveDetails', {
secret: wallet.getSecret(),
});
}}
title={loc.lnd.refill_external}
/>
<BlueListItem
<BlueListItemHooks
hideChevron
component={TouchableOpacity}
onPress={a => {
this.setState({ isManageFundsModalVisible: false }, async () => {
this.props.navigation.navigate('BuyBitcoin', {
wallet: this.state.wallet,
});
});
onPress={() => {
setIsManageFundsModalVisible(false);
navigateToBuyBitcoin();
}}
title={loc.lnd.refill_card}
/>
<BlueListItem
<BlueListItemHooks
title={loc.lnd.exchange}
hideChevron
component={TouchableOpacity}
onPress={a => {
this.setState({ isManageFundsModalVisible: false });
onPress={() => {
setIsManageFundsModalVisible(false);
Linking.openURL('https://zigzag.io/?utm_source=integration&utm_medium=bluewallet&utm_campaign=withdrawLink');
}}
/>
@ -435,87 +428,84 @@ export default class WalletTransactions extends Component {
);
};
renderMarketplaceButton = () => {
const navigateToBuyBitcoin = () => {
BuyBitcoin.navigate(wallet);
};
const renderMarketplaceButton = () => {
return Platform.select({
android: (
<TouchableOpacity
onPress={() => {
if (this.state.wallet.type === LightningCustodianWallet.type) {
this.props.navigation.navigate('LappBrowser', { fromSecret: this.state.wallet.getSecret(), fromWallet: this.state.wallet });
if (wallet.type === LightningCustodianWallet.type) {
navigate('LappBrowser', { fromSecret: wallet.getSecret(), fromWallet: wallet });
} else {
this.props.navigation.navigate('Marketplace', { fromWallet: this.state.wallet });
navigate('Marketplace', { fromWallet: wallet });
}
}}
style={styles.marketplaceButton1}
style={[styles.marketplaceButton1, stylesHook.marketplaceButton1]}
>
<Text style={styles.marketpalceText1}>marketplace</Text>
<Text style={[styles.marketpalceText1, stylesHook.marketpalceText1]}>marketplace</Text>
</TouchableOpacity>
),
ios:
this.state.wallet.getBalance() > 0 ? (
wallet.getBalance() > 0 ? (
<TouchableOpacity
onPress={async () => {
Linking.openURL('https://bluewallet.io/marketplace/');
}}
style={styles.marketplaceButton1}
style={[styles.marketplaceButton1, stylesHook.marketplaceButton1]}
>
<Icon name="external-link" size={18} type="font-awesome" color="#9aa0aa" />
<Text style={styles.marketpalceText2}>marketplace</Text>
<Text style={[styles.marketpalceText2, stylesHook.marketpalceText2]}>marketplace</Text>
</TouchableOpacity>
) : null,
});
};
renderLappBrowserButton = () => {
const renderLappBrowserButton = () => {
return (
<TouchableOpacity
onPress={() => {
this.props.navigation.navigate('LappBrowser', {
fromSecret: this.state.wallet.getSecret(),
fromWallet: this.state.wallet,
navigate('LappBrowser', {
fromSecret: wallet.getSecret(),
fromWallet: wallet,
url: 'https://duckduckgo.com',
});
}}
style={styles.marketplaceButton2}
style={[styles.marketplaceButton2, stylesHook.marketplaceButton2]}
>
<Text style={styles.marketpalceText1}>LApp Browser</Text>
<Text style={[styles.marketpalceText1, stylesHook.marketpalceText1]}>LApp Browser</Text>
</TouchableOpacity>
);
};
renderSellFiat = () => {
const renderSellFiat = () => {
return (
<TouchableOpacity
onPress={() =>
this.props.navigation.navigate('BuyBitcoin', {
wallet: this.state.wallet,
})
}
style={styles.marketplaceButton2}
>
<Text style={styles.marketpalceText1}>{loc.wallets.list_tap_here_to_buy}</Text>
<TouchableOpacity onPress={navigateToBuyBitcoin} style={[styles.marketplaceButton2, stylesHook.marketplaceButton2]}>
<Text style={[styles.marketpalceText1, stylesHook.marketpalceText1]}>{loc.wallets.list_tap_here_to_buy}</Text>
</TouchableOpacity>
);
};
onWalletSelect = async wallet => {
const onWalletSelect = async wallet => {
if (wallet) {
NavigationService.navigate('WalletTransactions', {
navigate('WalletTransactions', {
key: `WalletTransactions-${wallet.getID()}`,
});
/** @type {LightningCustodianWallet} */
let toAddress = false;
if (this.state.wallet.refill_addressess.length > 0) {
toAddress = this.state.wallet.refill_addressess[0];
if (wallet.refill_addressess.length > 0) {
toAddress = wallet.refill_addressess[0];
} else {
try {
await this.state.wallet.fetchBtcAddress();
toAddress = this.state.wallet.refill_addressess[0];
await wallet.fetchBtcAddress();
toAddress = wallet.refill_addressess[0];
} catch (Err) {
return alert(Err.message);
}
}
this.props.navigation.navigate('SendDetailsRoot', {
navigate('SendDetailsRoot', {
screen: 'SendDetails',
params: {
memo: loc.lnd.refill_lnd_balance,
@ -525,50 +515,36 @@ export default class WalletTransactions extends Component {
});
}
};
onFocus = () => {
this.redrawScreen();
this.props.navigation.setParams({ isLoading: false });
};
componentWillUnmount() {
clearInterval(this.interval);
this._unsubscribeFocus();
}
navigateToSendScreen = () => {
this.props.navigation.navigate('SendDetailsRoot', {
const navigateToSendScreen = () => {
navigate('SendDetailsRoot', {
screen: 'SendDetails',
params: {
fromWallet: this.state.wallet,
fromWallet: wallet,
},
});
};
renderItem = item => (
<BlueTransactionListItem item={item.item} itemPriceUnit={this.state.itemPriceUnit} timeElapsed={this.state.timeElapsed} />
);
const renderItem = item => <BlueTransactionListItem item={item.item} itemPriceUnit={itemPriceUnit} timeElapsed={timeElapsed} />;
onBarCodeRead = ret => {
if (!this.state.isLoading) {
this.setState({ isLoading: true }, () => {
this.setState({ isLoading: false });
const onBarCodeRead = ret => {
if (!isLoading) {
setIsLoading(true);
const params = {
fromSecret: this.state.wallet.getSecret(),
fromSecret: wallet.getSecret(),
// ScanLndInvoice actrually uses `fromSecret` so keeping it for now
uri: ret.data ? ret.data : ret,
fromWallet: this.state.wallet,
fromWallet: wallet,
};
if (this.state.wallet.chain === Chain.ONCHAIN) {
this.props.navigation.navigate('SendDetailsRoot', { screen: 'SendDetails', params });
if (wallet.chain === Chain.ONCHAIN) {
navigate('SendDetailsRoot', { screen: 'SendDetails', params });
} else {
this.props.navigation.navigate('ScanLndInvoiceRoot', { screen: 'ScanLndInvoice', params });
navigate('ScanLndInvoiceRoot', { screen: 'ScanLndInvoice', params });
}
});
}
setIsLoading(false);
};
choosePhoto = () => {
const choosePhoto = () => {
ImagePicker.launchImageLibrary(
{
title: null,
@ -580,7 +556,7 @@ export default class WalletTransactions extends Component {
const uri = Platform.OS === 'ios' ? response.uri.toString().replace('file://', '') : response.path.toString();
LocalQRCode.decode(uri, (error, result) => {
if (!error) {
this.onBarCodeRead({ data: result });
onBarCodeRead({ data: result });
} else {
alert(loc.send.qr_error_no_qrcode);
}
@ -590,11 +566,11 @@ export default class WalletTransactions extends Component {
);
};
copyFromClipbard = async () => {
this.onBarCodeRead({ data: await Clipboard.getString() });
const copyFromClipbard = async () => {
onBarCodeRead({ data: await Clipboard.getString() });
};
sendButtonLongPress = async () => {
const sendButtonLongPress = async () => {
const isClipboardEmpty = (await Clipboard.getString()).replace(' ', '').length === 0;
if (Platform.OS === 'ios') {
const options = [loc._.cancel, loc.wallets.list_long_choose, loc.wallets.list_long_scan];
@ -603,18 +579,18 @@ export default class WalletTransactions extends Component {
}
ActionSheet.showActionSheetWithOptions({ options, cancelButtonIndex: 0 }, buttonIndex => {
if (buttonIndex === 1) {
this.choosePhoto();
choosePhoto();
} else if (buttonIndex === 2) {
this.props.navigation.navigate('ScanQRCodeRoot', {
navigate('ScanQRCodeRoot', {
screen: 'ScanQRCode',
params: {
launchedBy: this.props.route.name,
onBarScanned: this.onBarScanned,
launchedBy: name,
onBarScanned: onBarCodeRead,
showFileImportButton: false,
},
});
} else if (buttonIndex === 3) {
this.copyFromClipbard();
copyFromClipbard();
}
});
} else if (Platform.OS === 'android') {
@ -626,16 +602,16 @@ export default class WalletTransactions extends Component {
},
{
text: loc.wallets.list_long_choose,
onPress: this.choosePhoto,
onPress: choosePhoto,
},
{
text: loc.wallets.list_long_scan,
onPress: () =>
this.props.navigation.navigate('ScanQRCodeRoot', {
navigate('ScanQRCodeRoot', {
screen: 'ScanQRCode',
params: {
launchedBy: this.props.route.name,
onBarScanned: this.onBarScanned,
launchedBy: name,
onBarScanned: onBarCodeRead,
showFileImportButton: false,
},
}),
@ -644,7 +620,7 @@ export default class WalletTransactions extends Component {
if (!isClipboardEmpty) {
buttons.push({
text: loc.wallets.list_long_clipboard,
onPress: this.copyFromClipbard,
onPress: copyFromClipbard,
});
}
ActionSheet.showActionSheetWithOptions({
@ -655,106 +631,94 @@ export default class WalletTransactions extends Component {
}
};
render() {
const { navigate } = this.props.navigation;
return (
<View style={styles.flex}>
<StatusBar barStyle="light-content" backgroundColor={WalletGradient.headerColorFor(this.props.route.params.wallet.type)} />
{this.state.wallet.chain === Chain.ONCHAIN && this.state.isHandOffUseEnabled && (
<StatusBar barStyle="light-content" backgroundColor={WalletGradient.headerColorFor(wallet.type)} />
{wallet.chain === Chain.ONCHAIN && isHandOffUseEnabled && (
<Handoff
title={`Bitcoin Wallet ${this.state.wallet.getLabel()}`}
title={`Bitcoin Wallet ${wallet.getLabel()}`}
type="io.bluewallet.bluewallet"
url={`https://blockpath.com/search/addr?q=${this.state.wallet.getXpub()}`}
url={`https://blockpath.com/search/addr?q=${wallet.getXpub()}`}
/>
)}
<BlueWalletNavigationHeader
wallet={this.state.wallet}
wallet={wallet}
onWalletUnitChange={wallet =>
InteractionManager.runAfterInteractions(async () => {
this.setState({ wallet, itemPriceUnit: wallet.getPreferredBalanceUnit() }, () =>
InteractionManager.runAfterInteractions(() => BlueApp.saveToDisk()),
);
setItemPriceUnit(wallet.getPreferredBalanceUnit());
BlueApp.saveToDisk();
})
}
onManageFundsPressed={() => {
if (this.state.wallet.getUserHasSavedExport()) {
this.setState({ isManageFundsModalVisible: true });
if (wallet.getUserHasSavedExport()) {
setIsManageFundsModalVisible(true);
} else {
BlueAlertWalletExportReminder({
onSuccess: async () => {
this.state.wallet.setUserHasSavedExport(true);
wallet.setUserHasSavedExport(true);
await BlueApp.saveToDisk();
this.setState({ isManageFundsModalVisible: true });
setIsManageFundsModalVisible(true);
},
onFailure: () =>
this.props.navigation.navigate('WalletExport', {
wallet: this.state.wallet,
navigate('WalletExport', {
wallet,
}),
});
}
}}
/>
<View style={styles.list}>
<View style={[styles.list, stylesHook.list]}>
<FlatList
ListHeaderComponent={this.renderListHeaderComponent}
ListHeaderComponent={renderListHeaderComponent}
onEndReachedThreshold={0.3}
onEndReached={async () => {
// pagination in works. in this block we will add more txs to flatlist
// so as user scrolls closer to bottom it will render mode transactions
if (this.getTransactions(Infinity).length < this.state.limit) {
if (getTransactions(Infinity).length < limit) {
// all list rendered. nop
return;
}
this.setState({
dataSource: this.getTransactions(this.state.limit + this.state.pageSize),
limit: this.state.limit + this.state.pageSize,
pageSize: this.state.pageSize * 2,
});
setDataSource(getTransactions(limit + pageSize));
setLimit(prev => prev + pageSize);
setPageSize(prev => prev * 2);
}}
ListFooterComponent={this.renderListFooterComponent}
ListFooterComponent={renderListFooterComponent}
ListEmptyComponent={
<ScrollView style={styles.flex} contentContainerStyle={styles.scrollViewContent}>
<Text numberOfLines={0} style={styles.emptyTxs}>
{(this.isLightning() && loc.wallets.list_empty_txs1_lightning) || loc.wallets.list_empty_txs1}
{(isLightning() && loc.wallets.list_empty_txs1_lightning) || loc.wallets.list_empty_txs1}
</Text>
{this.isLightning() && <Text style={styles.emptyTxsLightning}>{loc.wallets.list_empty_txs2_lightning}</Text>}
{isLightning() && <Text style={styles.emptyTxsLightning}>{loc.wallets.list_empty_txs2_lightning}</Text>}
{!this.isLightning() && (
<TouchableOpacity
onPress={() =>
this.props.navigation.navigate('BuyBitcoin', {
wallet: this.state.wallet,
})
}
style={styles.buyBitcoin}
>
{!isLightning() && (
<TouchableOpacity onPress={navigateToBuyBitcoin} style={styles.buyBitcoin}>
<Text style={styles.buyBitcoinText}>{loc.wallets.list_tap_here_to_buy}</Text>
</TouchableOpacity>
)}
</ScrollView>
}
onRefresh={() => this.refreshTransactions()}
refreshing={this.state.showShowFlatListRefreshControl}
data={this.state.dataSource}
extraData={this.state.timeElapsed}
keyExtractor={this._keyExtractor}
renderItem={this.renderItem}
onRefresh={refreshTransactions}
refreshing={isLoading}
data={dataSource}
extraData={timeElapsed}
keyExtractor={_keyExtractor}
renderItem={renderItem}
contentInset={{ top: 0, left: 0, bottom: 90, right: 0 }}
/>
{this.renderManageFundsModal()}
{renderManageFundsModal()}
</View>
<View style={styles.floatButtons}>
{(() => {
if (this.state.wallet.allowReceive()) {
if (wallet.allowReceive()) {
return (
<BlueReceiveButtonIcon
onPress={() => {
if (this.state.wallet.chain === Chain.OFFCHAIN) {
navigate('LNDCreateInvoiceRoot', { screen: 'LNDCreateInvoice', params: { fromWallet: this.state.wallet } });
if (wallet.chain === Chain.OFFCHAIN) {
navigate('LNDCreateInvoiceRoot', { screen: 'LNDCreateInvoice', params: { fromWallet: wallet } });
} else {
navigate('ReceiveDetails', { secret: this.state.wallet.getSecret() });
navigate('ReceiveDetails', { secret: wallet.getSecret() });
}
}}
/>
@ -763,26 +727,17 @@ export default class WalletTransactions extends Component {
})()}
{(() => {
if (
this.state.wallet.allowSend() ||
(this.state.wallet.type === WatchOnlyWallet.type &&
this.state.wallet.isHd() &&
this.state.wallet.getSecret().startsWith('zpub'))
) {
if (wallet.allowSend() || (wallet.type === WatchOnlyWallet.type && wallet.isHd() && wallet.getSecret().startsWith('zpub'))) {
return (
<BlueSendButtonIcon
onLongPress={this.sendButtonLongPress}
onLongPress={sendButtonLongPress}
onPress={() => {
if (this.state.wallet.chain === Chain.OFFCHAIN) {
navigate('ScanLndInvoiceRoot', { screen: 'ScanLndInvoice', params: { fromSecret: this.state.wallet.getSecret() } });
if (wallet.chain === Chain.OFFCHAIN) {
navigate('ScanLndInvoiceRoot', { screen: 'ScanLndInvoice', params: { fromSecret: wallet.getSecret() } });
} else {
if (
this.state.wallet.type === WatchOnlyWallet.type &&
this.state.wallet.isHd() &&
this.state.wallet.getSecret().startsWith('zpub')
) {
if (this.state.wallet.useWithHardwareWalletEnabled()) {
this.navigateToSendScreen();
if (wallet.type === WatchOnlyWallet.type && wallet.isHd() && wallet.getSecret().startsWith('zpub')) {
if (wallet.useWithHardwareWalletEnabled()) {
navigateToSendScreen();
} else {
Alert.alert(
loc.wallets.details_title,
@ -790,13 +745,10 @@ export default class WalletTransactions extends Component {
[
{
text: loc._.ok,
onPress: () => {
const wallet = this.state.wallet;
onPress: async () => {
wallet.setUseWithHardwareWalletEnabled(true);
this.setState({ wallet }, async () => {
await BlueApp.saveToDisk();
this.navigateToSendScreen();
});
navigateToSendScreen();
},
style: 'default',
},
@ -807,7 +759,7 @@ export default class WalletTransactions extends Component {
);
}
} else {
this.navigateToSendScreen();
navigateToSendScreen();
}
}
}}
@ -818,22 +770,10 @@ export default class WalletTransactions extends Component {
</View>
</View>
);
}
}
WalletTransactions.propTypes = {
navigation: PropTypes.shape({
navigate: PropTypes.func,
goBack: PropTypes.func,
setParams: PropTypes.func,
addListener: PropTypes.func,
}),
route: PropTypes.shape({
name: PropTypes.string,
params: PropTypes.object,
}),
};
export default WalletTransactions;
WalletTransactions.navigationOptions = ({ navigation, route }) => {
return {
headerRight: () => (

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>

View file

@ -2,7 +2,7 @@
jest.mock('react-native-watch-connectivity', () => {
return {
getIsWatchAppInstalled: jest.fn(),
getIsWatchAppInstalled: jest.fn(() => Promise.resolve(false)),
subscribeToMessages: jest.fn(),
updateApplicationContext: jest.fn(),
};