diff --git a/assets/images/SVG/Bitcoin.svg b/assets/images/SVG/Bitcoin.svg
index f8dd3d1c7..57d1b3596 100644
--- a/assets/images/SVG/Bitcoin.svg
+++ b/assets/images/SVG/Bitcoin.svg
@@ -1,3 +1,3 @@
diff --git a/components/Button.tsx b/components/Button.tsx
index 91d88b9a7..e5c48cfd2 100644
--- a/components/Button.tsx
+++ b/components/Button.tsx
@@ -10,11 +10,13 @@ interface ButtonProps {
secondary?: boolean;
tertiary?: boolean;
quaternary?: boolean;
+ quinary?: boolean;
iconOnly?: boolean;
adaptiveWidth?: boolean;
containerStyle?: any;
buttonStyle?: any;
noUppercase?: boolean;
+ disabled?: boolean;
}
function Button(props: ButtonProps) {
@@ -26,11 +28,13 @@ function Button(props: ButtonProps) {
secondary,
tertiary,
quaternary,
+ quinary,
iconOnly,
adaptiveWidth,
containerStyle,
buttonStyle,
- noUppercase
+ noUppercase,
+ disabled
} = props;
const newContainerStyle: any = adaptiveWidth
@@ -83,6 +87,8 @@ function Button(props: ButtonProps) {
: {
backgroundColor: iconOnly
? 'transparent'
+ : quinary
+ ? themeColor('secondary')
: quaternary
? themeColor('background')
: tertiary
@@ -101,6 +107,8 @@ function Button(props: ButtonProps) {
}
: {
color: iconOnly
+ ? themeColor('text')
+ : quinary
? themeColor('text')
: quaternary
? themeColor('text')
@@ -113,6 +121,7 @@ function Button(props: ButtonProps) {
}
onPress={onPress}
containerStyle={newContainerStyle}
+ disabled={disabled}
/>
);
}
diff --git a/components/PinPad.tsx b/components/PinPad.tsx
index f633a1539..b286da6ac 100644
--- a/components/PinPad.tsx
+++ b/components/PinPad.tsx
@@ -16,6 +16,7 @@ interface PinPadProps {
minLength?: number;
maxLength?: number;
numberHighlight?: boolean;
+ amount?: boolean;
}
export default function PinPad({
@@ -27,7 +28,8 @@ export default function PinPad({
hidePinLength = false,
minLength = 4,
maxLength = 8,
- numberHighlight = false
+ numberHighlight = false,
+ amount = false
}: PinPadProps) {
// PinPad state only depends on pin value length, not the actual pin/amount value
// Parent component to PinPad can store pin/amount value
@@ -65,7 +67,7 @@ export default function PinPad({
};
return (
-
+
{
@@ -163,16 +165,28 @@ export default function PinPad({
- {
- decrementPinValueLength();
- deleteValue();
- }}
- highlight={numberHighlight}
- style={styles.key}
- >
- {'<'}
-
+ {amount ? (
+ {
+ appendValue('.');
+ }}
+ highlight={numberHighlight}
+ style={styles.key}
+ >
+ {'.'}
+
+ ) : (
+ {
+ decrementPinValueLength();
+ deleteValue();
+ }}
+ highlight={numberHighlight}
+ style={styles.key}
+ >
+ {'<'}
+
+ )}
{
incrementPinValueLength();
@@ -183,18 +197,30 @@ export default function PinPad({
>
{pinNumbers[0]}
- {!hidePinLength && (
- {
- clearPinValueLength();
- clearValue();
- }}
- highlight={numberHighlight}
- style={styles.key}
- >
- C
-
- )}
+ {!hidePinLength &&
+ (amount ? (
+ {
+ decrementPinValueLength();
+ deleteValue();
+ }}
+ highlight={numberHighlight}
+ style={styles.key}
+ >
+ {'<'}
+
+ ) : (
+ {
+ clearPinValueLength();
+ clearValue();
+ }}
+ highlight={numberHighlight}
+ style={styles.key}
+ >
+ C
+
+ ))}
{!!hidePinLength && pinValueLength >= minLength && (
{
@@ -226,7 +252,7 @@ const styles = StyleSheet.create({
fontSize: 20,
fontFamily: 'Lato-Bold'
},
- bottom: {
+ pad: {
justifyContent: 'flex-end',
marginBottom: 25
},
diff --git a/components/UnitToggle.tsx b/components/UnitToggle.tsx
new file mode 100644
index 000000000..c5058de09
--- /dev/null
+++ b/components/UnitToggle.tsx
@@ -0,0 +1,43 @@
+import * as React from 'react';
+import { inject, observer } from 'mobx-react';
+
+import Button from './../components/Button';
+
+import UnitsStore from './../stores/UnitsStore';
+import SettingsStore from './../stores/SettingsStore';
+
+interface UnitToggleProps {
+ UnitsStore: UnitsStore;
+ SettingsStore: SettingsStore;
+ onToggle?: () => void;
+}
+
+@inject('UnitsStore', 'SettingsStore')
+@observer
+export default class UnitToggle extends React.Component {
+ render() {
+ const { UnitsStore, SettingsStore, onToggle } = this.props;
+ const { changeUnits, units } = UnitsStore;
+ const { settings } = SettingsStore;
+ const { fiat } = settings;
+
+ return (
+
+
+ );
+ }
+}
diff --git a/locales/en.json b/locales/en.json
index 2c28d4f5e..54e990049 100644
--- a/locales/en.json
+++ b/locales/en.json
@@ -1,6 +1,7 @@
{
"general.send": "Send",
"general.receive": "Receive",
+ "general.request": "Request",
"general.scan": "Scan",
"general.enableNfc": "Enable NFC",
"general.confirm": "Confirm",
diff --git a/views/Receive.tsx b/views/Receive.tsx
index 802949ba6..7dff0d893 100644
--- a/views/Receive.tsx
+++ b/views/Receive.tsx
@@ -79,6 +79,7 @@ export default class Receive extends React.Component<
navigation.getParam('lnurlParams');
const selectedIndex: number = navigation.getParam('selectedIndex');
+ const amount: string = navigation.getParam('amount');
if (lnurl) {
this.setState({
@@ -93,6 +94,12 @@ export default class Receive extends React.Component<
selectedIndex
});
}
+
+ if (amount) {
+ this.setState({
+ value: amount
+ });
+ }
}
getNewAddress = (params: any) => {
diff --git a/views/Wallet/DefaultPane.tsx b/views/Wallet/DefaultPane.tsx
new file mode 100644
index 000000000..e29414291
--- /dev/null
+++ b/views/Wallet/DefaultPane.tsx
@@ -0,0 +1,252 @@
+import * as React from 'react';
+import { Animated, View } from 'react-native';
+
+import { inject, observer } from 'mobx-react';
+
+import Button from '../../components/Button';
+import PinPad from '../../components/PinPad';
+import UnitToggle from '../../components/UnitToggle';
+import { WalletHeader } from '../../components/WalletHeader';
+
+import FiatStore from '../../stores/FiatStore';
+import UnitsStore from '../../stores/UnitsStore';
+import SettingsStore from '../../stores/SettingsStore';
+
+import { localeString } from '../../utils/LocaleUtils';
+import { themeColor } from '../../utils/ThemeUtils';
+
+interface DefaultPaneProps {
+ navigation: any;
+ FiatStore: FiatStore;
+ UnitsStore: UnitsStore;
+ SettingsStore: SettingsStore;
+}
+
+interface DefaultPaneState {
+ amount: string;
+}
+
+const MAX_LENGTH = 10;
+
+@inject('FiatStore', 'UnitsStore', 'SettingsStore')
+@observer
+export default class DefaultPane extends React.PureComponent<
+ DefaultPaneProps,
+ DefaultPaneState
+> {
+ shakeAnimation = new Animated.Value(0);
+ textAnimation = new Animated.Value(0);
+ state = {
+ amount: '0'
+ };
+
+ appendValue = (value: string) => {
+ const { amount } = this.state;
+ const { units } = this.props.UnitsStore;
+
+ let newAmount;
+
+ // limit decimal places depending on units
+ if (units === 'fiat') {
+ if (amount.split('.')[1] && amount.split('.')[1].length == 2)
+ return this.startShake();
+ }
+ if (units === 'sats') {
+ if (amount.split('.')[1] && amount.split('.')[1].length == 3)
+ return this.startShake();
+ }
+
+ if (amount.length >= MAX_LENGTH) {
+ newAmount = amount;
+ return this.startShake();
+ } else if (amount === '0') {
+ newAmount = value;
+ } else {
+ newAmount = `${amount}${value}`;
+ }
+
+ this.setState({
+ amount: newAmount
+ });
+ };
+
+ clearValue = () => {
+ this.setState({
+ amount: '0'
+ });
+ };
+
+ deleteValue = () => {
+ const { amount } = this.state;
+
+ let newAmount;
+
+ if (amount.length === 1) {
+ newAmount = '0';
+ } else {
+ newAmount = amount.substr(0, amount.length - 1);
+ }
+
+ this.setState({
+ amount: newAmount
+ });
+ };
+
+ amountSize = () => {
+ switch (this.state.amount.length) {
+ case 1:
+ case 2:
+ return 80;
+ break;
+ case 3:
+ case 4:
+ return 65;
+ break;
+ case 5:
+ case 6:
+ return 55;
+ break;
+ case 7:
+ return 50;
+ break;
+ case 8:
+ return 45;
+ break;
+ default:
+ return 35;
+ }
+ };
+
+ startShake = () => {
+ Animated.parallel([
+ Animated.sequence([
+ Animated.timing(this.textAnimation, {
+ toValue: 1,
+ duration: 100,
+ useNativeDriver: false
+ }),
+ Animated.timing(this.textAnimation, {
+ toValue: 0,
+ duration: 1000,
+ useNativeDriver: false
+ })
+ ]).start(),
+ Animated.sequence([
+ Animated.timing(this.shakeAnimation, {
+ toValue: 10,
+ duration: 100,
+ useNativeDriver: true
+ }),
+ Animated.timing(this.shakeAnimation, {
+ toValue: -10,
+ duration: 100,
+ useNativeDriver: true
+ }),
+ Animated.timing(this.shakeAnimation, {
+ toValue: 10,
+ duration: 100,
+ useNativeDriver: true
+ }),
+ Animated.timing(this.shakeAnimation, {
+ toValue: 0,
+ duration: 100,
+ useNativeDriver: true
+ })
+ ]).start()
+ ]);
+ };
+
+ render() {
+ const { FiatStore, SettingsStore, navigation } = this.props;
+ const { amount } = this.state;
+
+ const color = this.textAnimation.interpolate({
+ inputRange: [0, 1],
+ outputRange: [themeColor('text'), 'red']
+ });
+
+ return (
+
+
+
+
+
+ {FiatStore.numberWithCommas(amount)}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+ }
+}
diff --git a/views/Wallet/Wallet.tsx b/views/Wallet/Wallet.tsx
index ef7fd1192..d7169c6fd 100644
--- a/views/Wallet/Wallet.tsx
+++ b/views/Wallet/Wallet.tsx
@@ -14,6 +14,7 @@ import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import RNRestart from 'react-native-restart';
import ChannelsPane from '../Channels/ChannelsPane';
+import DefaultPane from './DefaultPane';
import MainPane from './MainPane';
import Button from './../../components/Button';
@@ -35,6 +36,7 @@ import FiatStore from './../../stores/FiatStore';
import UnitsStore from './../../stores/UnitsStore';
import UTXOsStore from './../../stores/UTXOsStore';
+import Bitcoin from './../../assets/images/SVG/Bitcoin.svg';
import Temple from './../../assets/images/SVG/Temple.svg';
import ChannelsIcon from './../../assets/images/SVG/Channels.svg';
import CaretUp from './../../assets/images/SVG/Caret Up.svg';
@@ -289,6 +291,19 @@ export default class Wallet extends React.Component {
);
};
+ const DefaultScreen = () => {
+ return (
+
+
+
+ );
+ };
+
const ChannelsScreen = () => {
return (
{
{!connecting && !loginRequired && (
({
tabBarIcon: ({ color }) => {
+ if (route.name === 'Default') {
+ return ;
+ }
if (route.name === 'Wallet') {
return ;
}
@@ -347,6 +366,10 @@ export default class Wallet extends React.Component {
name="Wallet"
component={WalletScreen}
/>
+
{RESTUtils.supportsChannelManagement() &&
!error ? (