diff --git a/package-lock.json b/package-lock.json
index b985cf2..67f9004 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2957,6 +2957,15 @@
"resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.5.0.tgz",
"integrity": "sha1-sGhzk0vF40T+9hGhlqb6rgruAVo="
},
+ "connected-react-router": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/connected-react-router/-/connected-react-router-5.0.1.tgz",
+ "integrity": "sha512-0QwWYPRGZQ7f284lmqc5kwC4T3iW3zrAH3zzi6uUMzTOxbA+mn38tAgMOoVo9m3pbskvONFtXiajgVkCElE9EQ==",
+ "requires": {
+ "immutable": "^3.8.1",
+ "seamless-immutable": "^7.1.3"
+ }
+ },
"console-browserify": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz",
@@ -6851,6 +6860,11 @@
"resolved": "https://registry.npmjs.org/immer/-/immer-1.7.2.tgz",
"integrity": "sha512-4Urocwu9+XLDJw4Tc6ZCg7APVjjLInCFvO4TwGsAYV5zT6YYSor14dsZR0+0tHlDIN92cFUOq+i7fC00G5vTxA=="
},
+ "immutable": {
+ "version": "3.8.2",
+ "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.8.2.tgz",
+ "integrity": "sha1-wkOZUUVbs5kT2vKBN28VMOEErfM="
+ },
"import-cwd": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-2.1.0.tgz",
@@ -12805,6 +12819,11 @@
"ajv-keywords": "^3.1.0"
}
},
+ "seamless-immutable": {
+ "version": "7.1.4",
+ "resolved": "https://registry.npmjs.org/seamless-immutable/-/seamless-immutable-7.1.4.tgz",
+ "integrity": "sha512-XiUO1QP4ki4E2PHegiGAlu6r82o5A+6tRh7IkGGTVg/h+UoeX4nFBeCGPOhb4CYjvkqsfm/TUtvOMYC1xmV30A=="
+ },
"select-hose": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
diff --git a/package.json b/package.json
index 0ca79b6..4dc6237 100644
--- a/package.json
+++ b/package.json
@@ -2,6 +2,7 @@
"name": "boltz-frontend",
"version": "1.0.0",
"dependencies": {
+ "connected-react-router": "^5.0.1",
"prop-types": "^15.6.2",
"react": "^16.6.3",
"react-dom": "^16.6.3",
diff --git a/src/components/input/index.js b/src/components/input/index.js
index 65bbe65..2e00434 100644
--- a/src/components/input/index.js
+++ b/src/components/input/index.js
@@ -12,45 +12,56 @@ const styles = theme => ({
backgroundColor: theme.colors.lightGrey,
width: '200px',
height: '50px',
- '&:focus': {
- outline: 'none',
- },
+ outline: p => (p.error ? '1px solid red' : 'none'),
},
});
class Input extends React.Component {
constructor(props) {
super(props);
- this.state = { text: 0 };
+ this.state = { value: 0 };
}
onChange = e => {
- if (e.target.value >= 0) {
- this.setState({ text: e.target.value });
+ if (e.target.value) {
+ this.setState({ value: e.target.value });
+ this.props.onChange && this.props.onChange(e.target.value);
}
};
render() {
- const { classes, isText, style, disable } = this.props;
+ const { classes, style, disable, min, step, value, max } = this.props;
return (
this.onChange(e)}
- value={this.state.text}
- type={isText ? 'text' : 'number'}
+ value={value ? value : this.state.value}
+ type={'number'}
/>
);
}
}
+Input.defaultProps = {
+ min: 0,
+ step: 1,
+};
+
Input.propTypes = {
classes: PropTypes.object.isRequired,
- isText: PropTypes.bool.isRequired,
+ onChange: PropTypes.func,
style: PropTypes.object,
disable: PropTypes.bool,
+ error: PropTypes.bool,
+ min: PropTypes.number,
+ max: PropTypes.number,
+ value: PropTypes.number,
+ step: PropTypes.number,
};
export default injectSheet(styles)(Input);
diff --git a/src/views/landingPage/landingPage.js b/src/components/landingpage/index.js
similarity index 84%
rename from src/views/landingPage/landingPage.js
rename to src/components/landingpage/index.js
index a4c9ed9..18b4ba3 100644
--- a/src/views/landingPage/landingPage.js
+++ b/src/components/landingpage/index.js
@@ -33,7 +33,7 @@ const styles = theme => ({
},
});
-const LandingPage = ({ classes, toggleSwapMode }) => {
+const LandingPage = ({ classes, toggleSwapMode, setSwapAmount }) => {
return (
@@ -52,16 +52,22 @@ const LandingPage = ({ classes, toggleSwapMode }) => {
- toggleSwapMode()} />
+ {
+ setSwapAmount(sent, received);
+ toggleSwapMode();
+ }}
+ />
);
};
LandingPage.propTypes = {
- classes: PropTypes.object,
+ classes: PropTypes.object.isRequired,
inSwapMode: PropTypes.bool,
toggleSwapMode: PropTypes.func,
+ setSwapAmount: PropTypes.func,
};
export default injectSheet(styles)(LandingPage);
diff --git a/src/components/swaptab/index.js b/src/components/swaptab/index.js
index 33573a9..f19e0a7 100644
--- a/src/components/swaptab/index.js
+++ b/src/components/swaptab/index.js
@@ -5,6 +5,7 @@ import View from '../view';
import Input from '../input';
import DropDown from '../dropdown';
import Text, { InfoText } from '../text';
+import { MIN, MAX, FEE } from '../../constants/fees';
import { FaArrowRight } from 'react-icons/fa';
const types = ['BTC', 'T-BTC'];
@@ -67,32 +68,71 @@ const styles = theme => ({
},
});
-const SwapTab = ({ classes, onClick }) => (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+class SwapTab extends React.Component {
+ state = {
+ sent: 0,
+ received: 0,
+ error: false,
+ };
+
+ setSwapData = sent => {
+ if (sent > MAX || sent < MIN) {
+ this.setState({
+ error: true,
+ });
+ } else {
+ this.setState({
+ sent,
+ received: Number.parseFloat(sent - FEE).toFixed(4),
+ error: false,
+ });
+ }
+ };
+
+ shouldSubmit = () => {
+ const { error, sent, received } = this.state;
+ if (!error && sent !== 0) {
+ this.props.onClick(sent, received);
+ }
+ };
+
+ render() {
+ const { classes } = this.props;
+ return (
+
+
+
+
+
+
+
+
+
+
+ this.setSwapData(e)}
+ />
+
+
+
+
+
+
+
+
+ this.shouldSubmit()}>
+
+
+
-
- onClick()}>
-
-
-
-
-);
+ );
+ }
+}
+
SwapTab.propTypes = {
classes: PropTypes.object,
onClick: PropTypes.func,
diff --git a/src/components/taskbar/index.js b/src/components/taskbar/index.js
index b6ea371..3ab9916 100644
--- a/src/components/taskbar/index.js
+++ b/src/components/taskbar/index.js
@@ -28,7 +28,7 @@ const TaskBar = ({ classes }) => (
alt="logo"
/>
-
+
+ combineReducers({
+ router: connectRouter(history),
+ swapReducer,
+ refundReducer,
+ });
-export default rootReducer;
+export default createRootReducer;
diff --git a/src/views/index.js b/src/views/index.js
index d698586..eb51eca 100644
--- a/src/views/index.js
+++ b/src/views/index.js
@@ -1,12 +1,12 @@
import React, { Component } from 'react';
import { Provider } from 'react-redux';
import { ThemeProvider, preset, jss } from 'react-jss';
-import { BrowserRouter as Router, Route } from 'react-router-dom';
-import store from '../state';
+import { Route, Switch } from 'react-router-dom';
+import { ConnectedRouter } from 'connected-react-router';
+import store, { history } from '../state';
import theme from '../constants/theme';
import Container from '../components/container';
-import LandingPage from '../views/landingPage';
import Swap from '../views/swap';
import Refund from '../views/refund';
@@ -16,16 +16,17 @@ class App extends Component {
render() {
return (
-
+
-
-
-
-
+
+
+
+
+
-
+
);
}
diff --git a/src/views/landingPage/index.js b/src/views/landingPage/index.js
deleted file mode 100644
index cc66d70..0000000
--- a/src/views/landingPage/index.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import { connect } from 'react-redux';
-import * as actions from './landingPageActions';
-import LandingPage from './landingPage';
-
-const mapStateToProps = state => ({
- inSwapMode: state.swapReducer.inSwapMode,
-});
-
-const mapDispatchToProps = dispatch => ({
- toggleSwapMode: () => dispatch(actions.toggleSwapMode()),
-});
-
-export default connect(
- mapStateToProps,
- mapDispatchToProps
-)(LandingPage);
diff --git a/src/views/landingPage/landingPageActions.js b/src/views/landingPage/landingPageActions.js
deleted file mode 100644
index 734b44e..0000000
--- a/src/views/landingPage/landingPageActions.js
+++ /dev/null
@@ -1,5 +0,0 @@
-import * as actionTypes from '../../constants/actions';
-
-export const toggleSwapMode = () => ({
- type: actionTypes.ENTER_SWAP_MODE,
-});
diff --git a/src/views/refund/index.js b/src/views/refund/index.js
index fcf8bc3..4e2d9d0 100644
--- a/src/views/refund/index.js
+++ b/src/views/refund/index.js
@@ -1,4 +1,5 @@
import { connect } from 'react-redux';
+import { push } from 'connected-react-router';
import Refund from './refund';
import * as actions from './refundActions';
@@ -8,6 +9,7 @@ const mapStateToProps = state => ({
const mapDispatchToProps = dispatch => ({
toggleRefundMode: () => dispatch(actions.toggleRefundMode()),
+ push: path => dispatch(push(path)),
});
export default connect(
diff --git a/src/views/refund/refund.js b/src/views/refund/refund.js
index caff6f7..f8ff2fb 100644
--- a/src/views/refund/refund.js
+++ b/src/views/refund/refund.js
@@ -3,7 +3,6 @@ import PropTypes from 'prop-types';
import injectSheet from 'react-jss';
import { FaArrowRight } from 'react-icons/fa';
import Background from '../../components/background';
-import TaskBar from '../../components/taskbar';
import StepsWizard from '../../components/stepswizard';
import View from '../../components/view';
import { StepOne, StepTwo, StepFour } from './steps';
@@ -29,49 +28,64 @@ Controls.propTypes = {
text: PropTypes.string,
};
-const Refund = ({ classes, inRefundMode, toggleRefundMode }) => (
-
-
-
- toggleRefundMode()}
- message={'Are you sure?'}
- >
-
- } />
- } />
- } />
- } />
-
-
- }
- />
- }
- />
- }
- />
- }
- />
-
-
-
-
-);
+class Refund extends React.Component {
+ UNSAFE_componentWillMount() {
+ this.props.toggleRefundMode();
+ }
+
+ componentWillUnmount() {
+ this.props.toggleRefundMode();
+ }
+
+ render() {
+ const { classes, inRefundMode, push } = this.props;
+ return (
+
+
+ {
+ push('/');
+ }}
+ message={'Are you sure?'}
+ >
+
+ } />
+ } />
+ } />
+ } />
+
+
+ }
+ />
+ }
+ />
+ }
+ />
+ }
+ />
+
+
+
+
+ );
+ }
+}
Refund.propTypes = {
classes: PropTypes.object,
+ push: PropTypes.func,
inRefundMode: PropTypes.bool,
toggleRefundMode: PropTypes.func,
};
diff --git a/src/views/swap/index.js b/src/views/swap/index.js
index ece981b..103da8b 100644
--- a/src/views/swap/index.js
+++ b/src/views/swap/index.js
@@ -1,13 +1,18 @@
import { connect } from 'react-redux';
import { compose } from 'redux';
import { withRouter } from 'react-router-dom';
+import { toggleSwapMode, setSwapAmount } from './swapActions';
import Swap from './swap';
const mapStateToProps = state => ({
inSwapMode: state.swapReducer.inSwapMode,
+ swapInfo: state.swapReducer.swapInfo,
});
-const mapDispatchToProps = () => {};
+const mapDispatchToProps = dispatch => ({
+ toggleSwapMode: () => dispatch(toggleSwapMode()),
+ setSwapAmount: (sent, received) => dispatch(setSwapAmount(sent, received)),
+});
export default compose(
connect(
diff --git a/src/views/swap/steps.js b/src/views/swap/steps.js
index 6372e70..c8641dc 100644
--- a/src/views/swap/steps.js
+++ b/src/views/swap/steps.js
@@ -1,6 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import injectSheet from 'react-jss';
+import { Link } from 'react-router-dom';
import qr from '../../asset/icons/qr_code.png';
import { FaCheckCircle, FaBolt } from 'react-icons/fa';
import View from '../../components/view';
@@ -29,12 +30,12 @@ const stepOneStyles = () => ({
},
});
-const StyledStepOne = ({ classes }) => (
+const StyledStepOne = ({ classes, value }) => (
Paste a Bitcoin lightning {' '}
invoice of
- 0.0049 T-BTC to recieve it.
+ {value} T-BTC to recieve it.
lnbc1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpl2pkx2ctnv5
@@ -46,6 +47,7 @@ const StyledStepOne = ({ classes }) => (
StyledStepOne.propTypes = {
classes: PropTypes.object.isRequired,
+ value: PropTypes.number.isRequired,
};
export const StepOne = injectSheet(stepOneStyles)(StyledStepOne);
@@ -83,7 +85,7 @@ const stepTwoStyles = () => ({
},
});
-const StyledStepTwo = ({ classes }) => (
+const StyledStepTwo = ({ classes, value }) => (
@@ -94,7 +96,7 @@ const StyledStepTwo = ({ classes }) => (
fontSize: '30px',
}}
>
- Send 0.005 T-BTC
+ Send {value} T-BTC
on Bitcoin
blockchain address:
@@ -110,6 +112,7 @@ const StyledStepTwo = ({ classes }) => (
StyledStepTwo.propTypes = {
classes: PropTypes.object.isRequired,
+ value: PropTypes.number.isRequired,
};
export const StepTwo = injectSheet(stepTwoStyles)(StyledStepTwo);
@@ -185,3 +188,58 @@ StyledStepFour.propTypes = {
};
export const StepFour = injectSheet(stepFourStyles)(StyledStepFour);
+
+const errorStyles = theme => ({
+ wrapper: {
+ width: '700px',
+ height: '400px',
+ boxShadow: '0px 0px 30px -6px rgba(0,0,0,0.52)',
+ backgroundColor: theme.colors.white,
+ flexDirection: 'column',
+ },
+ content: {
+ width: '100%',
+ height: '80%',
+ justifyContent: 'center',
+ alignItems: 'center',
+ },
+ button: {
+ width: '100%',
+ height: '20%',
+ backgroundColor: theme.colors.matisseBlue,
+ '&:hover': {
+ cursor: 'pointer',
+ },
+ },
+ info: {
+ fontSize: '30px',
+ },
+ link: {
+ flex: 1,
+ display: 'flex',
+ justifyContent: 'center',
+ alignItems: 'center',
+ textDecoration: 'none',
+ color: theme.colors.white,
+ },
+});
+
+const StyledError = ({ classes }) => (
+
+
+ You cannot begin a swap.
+
+
+
+ Back
+
+
+
+);
+
+StyledError.propTypes = {
+ classes: PropTypes.object.isRequired,
+ text: PropTypes.string,
+};
+
+export const Error = injectSheet(errorStyles)(StyledError);
diff --git a/src/views/swap/swap.js b/src/views/swap/swap.js
index 5dbd50f..66794f0 100644
--- a/src/views/swap/swap.js
+++ b/src/views/swap/swap.js
@@ -5,6 +5,7 @@ import { FaArrowRight } from 'react-icons/fa';
import View from '../../components/view';
import BackGround from '../../components/background';
import StepsWizard from '../../components/stepswizard';
+import LandingPage from '../../components/landingpage';
import { StepOne, StepTwo, StepThree, StepFour } from './steps';
const styles = () => ({
@@ -28,46 +29,67 @@ Controls.propTypes = {
text: PropTypes.string,
};
-const Swap = ({ classes, history, inSwapMode, toggleSwapMode }) => {
- if (!inSwapMode) {
- history.replace('/');
- }
+const Swap = ({
+ classes,
+ inSwapMode,
+ toggleSwapMode,
+ setSwapAmount,
+ swapInfo,
+}) => {
return (
- toggleSwapMode()}
- alertOnExit={inSwapMode}
- // TODO: change state isSwapMode
- message={'Are you sure?'}
- >
-
- } />
- } />
- } />
- } />
-
-
- }
- />
- }
- />
- }
- />
- }
- />
-
-
+ {inSwapMode ? (
+ {
+ const x = window.confirm('Sure you want to exit');
+ if (x) {
+ setSwapAmount(null, null);
+ toggleSwapMode();
+ }
+ }}
+ alertOnExit={inSwapMode}
+ message={'Are you sure?'}
+ >
+
+ }
+ />
+ }
+ />
+ } />
+ } />
+
+
+ }
+ />
+ }
+ />
+ }
+ />
+ }
+ />
+
+
+ ) : (
+
+ )}
);
@@ -77,7 +99,9 @@ Swap.propTypes = {
classes: PropTypes.object.isRequired,
history: PropTypes.object.isRequired,
inSwapMode: PropTypes.bool.isRequired,
+ swapInfo: PropTypes.object,
toggleSwapMode: PropTypes.func,
+ setSwapAmount: PropTypes.func,
};
export default injectSheet(styles)(Swap);
diff --git a/src/views/swap/swapActions.js b/src/views/swap/swapActions.js
new file mode 100644
index 0000000..ce7211c
--- /dev/null
+++ b/src/views/swap/swapActions.js
@@ -0,0 +1,17 @@
+import * as actionTypes from '../../constants/actions';
+
+export const toggleSwapMode = () => ({
+ type: actionTypes.ENTER_SWAP_MODE,
+});
+
+/**
+ * @param sent amount sent
+ * @param received amount received
+ */
+export const setSwapAmount = (sent, received) => ({
+ type: actionTypes.SET_SWAP_AMOUNT,
+ payload: {
+ sent,
+ received,
+ },
+});
diff --git a/src/views/landingPage/landingPageReducer.js b/src/views/swap/swapReducer.js
similarity index 59%
rename from src/views/landingPage/landingPageReducer.js
rename to src/views/swap/swapReducer.js
index b850334..8e3f102 100644
--- a/src/views/landingPage/landingPageReducer.js
+++ b/src/views/swap/swapReducer.js
@@ -2,6 +2,10 @@ import * as actionTypes from '../../constants/actions';
const initalState = {
inSwapMode: false,
+ swapInfo: {
+ sent: null,
+ received: null,
+ },
};
const reducer = (state = initalState, action) => {
@@ -11,6 +15,14 @@ const reducer = (state = initalState, action) => {
...state,
inSwapMode: !state.inSwapMode,
};
+ case actionTypes.SET_SWAP_AMOUNT:
+ return {
+ ...state,
+ swapInfo: {
+ sent: action.payload.sent,
+ received: action.payload.received,
+ },
+ };
default:
return state;
}