Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow for send invoke calls from websites the user visits #27

Merged
merged 6 commits into from
Jan 3, 2018
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions src/app/Root.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,25 @@ import PropTypes from 'prop-types'
import { BrowserRouter } from 'react-router-dom'
import { Provider } from 'react-redux'
import App from './containers/App'
import PopupWindow from './containers/PopupWindow'

export default class Root extends Component {
render () {
const { store } = this.props
const { store, isPopupWindow } = this.props
return (
<Provider store={store}>
<BrowserRouter>
<App />
{ isPopupWindow
? <PopupWindow />
: <App />
}
</BrowserRouter>
</Provider>
)
}
}

Root.propTypes = {
store: PropTypes.object.isRequired
store: PropTypes.object.isRequired,
isPopupWindow: PropTypes.bool.isRequired
}
4 changes: 4 additions & 0 deletions src/app/components/ContentWrapper/ContentWrapper.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions src/app/components/ContentWrapper/ContentWrapper.css.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/app/components/ContentWrapper/ContentWrapper.scss
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@
flex-direction: column;
}

.infoText {
word-wrap: break-word;
width:100%;
}

.fancyBox {
width: 83%;
height: 90%;
Expand Down
10 changes: 7 additions & 3 deletions src/app/components/Header/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,17 @@ import style from './Header.css'

export default class Header extends Component {
render() {
const { noMenu } = this.props

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code might more clear if it was written in a more declarative way. Something like: const { hasMenu = false } = this.props. This way, we can be more truthy, and we don't need to add configuration to take away a thing we don't need, we only have to add add configuration for a thing we want.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call. I'll update it.


return (
<div className={style.header}>
<NetworkSwitcher />
<div className={style.titleBar}>
<div className={style.menuNavWrapper}>
<MainNav />
</div>
{ noMenu ||

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than running this check inline in the JSP, you could break it out and keep your template code a little cleaner.

const menu = if (hasMenu) <div and mainMenu />

Then in the JSP, something like { menu } instead if the check. Keeping logic out of template code is best for testing when we get that running.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I like it.

<div className={style.menuNavWrapper}>
<MainNav />
</div>
}
<div className={style.titleWrapper}>
<span className={style.titleName}>NeoLink</span>
</div>
Expand Down
45 changes: 45 additions & 0 deletions src/app/containers/PopupWindow/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import React, { Component } from 'react'
import SendInvokeReadonly from '../SendInvoke/SendInvokeReadonly'

import Header from '../../components/Header'
import ContentWrapper from '../../components/ContentWrapper'

import style from '../App/App.css'

export default class PopupWindow extends Component {
state = {
transaction: null,
success: false
}

componentDidMount () {
this.port = chrome.runtime.connect({name: "popup"})
this.port.onMessage.addListener((message) => {
if (message.operation === 'sendInvoke') {
this.setState({
transaction: message.txInfo
})
}
})
}

onSuccess = (response) => {
this.port.postMessage({ msg: 'sendInvokeResponse', status: 'success', txid: response.txid })
}

render () {
const { transaction } = this.state

return (
<div className={style.popup}>
<Header noMenu />
<ContentWrapper>
{ !transaction
? <div>Loading</div>
: <SendInvokeReadonly transaction={transaction} onSuccess={this.onSuccess} />
}
</ContentWrapper>
</div>
)
}
}
16 changes: 13 additions & 3 deletions src/app/containers/SendInvoke/SendInvoke.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
.transactionId {
word-wrap: break-word;
}
.label {
width:100%;
font-weight: bold;
}

.successBox {
width: 200px;
}

.entryItem {
width: 80%;
padding-bottom: 10px;
}
122 changes: 122 additions & 0 deletions src/app/containers/SendInvoke/SendInvokeReadonly.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import React, { Component } from 'react'

import { connect } from 'react-redux'

import Button from 'preact-material-components/Button'
import 'preact-material-components/Button/style.css'
import 'preact-material-components/Theme/style.css'

import { callInvoke } from '../../utils/neonWrappers';

import style from './SendInvoke.css'
import globalStyle from '../../components/ContentWrapper/ContentWrapper.css'

@connect(
state => ({
network: state.network,
account: state.account
})
)

export default class SendInvokeReadonly extends Component {
state = {
loading: false,
errorMsg: '',
txid: ''
}

handleSubmit = (event) => {
event.preventDefault()
const {network, account, transaction, onSuccess } = this.props

this.setState({
loading: true,
errorMsg: '',
txid: ''
})

const config = {
scriptHash: transaction.scriptHash,
operation: transaction.operation,
arg1: transaction.args[0],
arg2: transaction.args[1],
amount: transaction.amount,
assetType: transaction.type
}

callInvoke(network, account, config)
.then((c) => {
if (c.response.result === true) {
this.setState({
loading: false,
txid: c.response.txid
})

if (onSuccess) {
onSuccess(c.response)
}
} else {
this.setState({
loading: false,
errorMsg: 'Invoke failed'
})
}
})
.catch((e) => {
console.log('e', e)
this.setState({
loading: false,
errorMsg: 'Invoke failed'
})
})
}

render() {
const { loading, txid, errorMsg } = this.state
const { transaction } = this.props

return (
<div>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I pointed this out in my big issue the other day, but it's a nice way to break up your concerns if you keep your view JSX in a different component than the connected portion which deals with the redux store. This makes testing easier, separates the implementation from redux in case you want to use the component in a non-connected way, want to remove redux and use a different state management system, or want to keep your testing simple.

This would also allow the component to be written as a stateless functional component which is really lightweight and easy to test.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you saying we should do something like neon-wallet does?
https://github.com/CityOfZion/neon-wallet/blob/dev/app/containers/DisplayWalletKeys/index.js
https://github.com/CityOfZion/neon-wallet/blob/dev/app/containers/DisplayWalletKeys/DisplayWalletKeys.jsx

That separates redux and I'm ok with that. Just haven't planned out how to organize everything quite yet. We could follow their pattern or do something a bit different.

Or you're saying we should completely separate all jsx from even the react state?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, the way neon-wallet does it is fine!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@austinhinderer and I have agreed we'll tackle this as part of refactoring when testing is in place.

<form onSubmit={this.handleSubmit} style="padding-top:35px;">
<div className={style.entryItem}>
<span className={style.label}>Script Hash:</span>
<span className={globalStyle.infoText}>{ transaction.scriptHash }</span>
</div>
<div className={style.entryItem}>
<span className={style.label}>Operation:</span>
<span className={globalStyle.infoText}>{ transaction.operation }</span>
</div>
<div className={style.entryItem}>
<span className={style.label}>Argument 1:</span>
<span className={globalStyle.infoText}>{ transaction.args[0] }</span>
</div>
<div className={style.entryItem}>
<span className={style.label}>Argument 2:</span>
<span className={globalStyle.infoText}>{ transaction.args[1] }</span>
</div>
<div className={style.entryItem}>
<span className={style.label}>Amount:</span>
<span className={globalStyle.infoText}>{ transaction.amount }</span>
</div>
<div className={style.entryItem}>
<span className={style.label}>Asset:</span>
<span className={globalStyle.infoText}>{ transaction.type }</span>
</div>
<Button raised ripple disabled={this.state.loading || this.state.success}>Invoke</Button>
</form>
{ txid &&
<div className={style.successBox}>
<div>Success!</div>
<span className={globalStyle.infoText}>txid: { txid }</span>
</div>
}
{ loading &&
<div>Loading...</div>
}
{ errorMsg !== '' &&
<div>ERROR: {errorMsg}</div>
}
</div>
)
}
}
Loading