@@ -4,13 +4,26 @@ import * as ob from 'urbit-ob'
4import * as azimuth from 'azimuth-js'
4import * as azimuth from 'azimuth-js'
6import { BRIDGE_ERROR } from '../lib/error'
6import { BRIDGE_ERROR } from '../lib/error'
7−import { sendMail } from '../lib/inviteMail'
7+import { hasReceived, sendMail } from '../lib/inviteMail'
8+import { Row, Col, Warning, Button, H1, H3,
8−import { Row, Col, Input, InnerLabel } from '../components/Base'
10+} from '../components/Base'
9import StatelessTransaction from '../components/StatelessTransaction'
11import StatelessTransaction from '../components/StatelessTransaction'
11// for wallet generation
13// for wallet generation
12import * as wg from '../../walletgen/lib/lib'
14import * as wg from '../../walletgen/lib/lib'
16+// for transaction generation and signing
19+ sendSignedTransaction,
20+ waitForTransactionConfirm,
21+ isTransactionConfirmed,
24+import * as tank from '../lib/tank'
25+import Tx from 'ethereumjs-tx'
14class InvitesSend extends React.Component {
27class InvitesSend extends React.Component {
16 constructor(props) {
29 constructor(props) {
@@ -22,7 +35,8 @@ class InvitesSend extends React.Component {
22 haveInvited: Nothing(),
35 haveInvited: Nothing(),
23 randomPlanet: Nothing(),
36 randomPlanet: Nothing(),
24 inviteWallet: Nothing(),
37 inviteWallet: Nothing(),
25− targetEmail: ''
38+ targetEmail: '',
39+ targetHasReceived: Nothing()
28 this.findInvited = this.findInvited.bind(this);
42 this.findInvited = this.findInvited.bind(this);
@@ -30,8 +44,8 @@ class InvitesSend extends React.Component {
30 this.generateWallet = this.generateWallet.bind(this);
44 this.generateWallet = this.generateWallet.bind(this);
31 this.canGenerate = this.canGenerate.bind(this);
45 this.canGenerate = this.canGenerate.bind(this);
32 this.handleEmailInput = this.handleEmailInput.bind(this);
46 this.handleEmailInput = this.handleEmailInput.bind(this);
33− this.txnConfirmation = this.txnConfirmation.bind(this);
34 this.createUnsignedTxn = this.createUnsignedTxn.bind(this);
47 this.createUnsignedTxn = this.createUnsignedTxn.bind(this);
48+ this.beforeSend = this.beforeSend.bind(this);
37 componentDidMount() {
51 componentDidMount() {
@@ -102,19 +116,33 @@ class InvitesSend extends React.Component {
104 handleEmailInput(email) {
118 handleEmailInput(email) {
105− //TODO simple .*@.*\..* email validation
119+ let newState = { targetEmail: email };
106− this.setState({ targetEmail: email });
120+ if (email.match(/.*@.*\...+/)) {
123+ console.log('hasReceived', res);
124+ this.setState({ targetHasReceived: Just(res) });
127+ newState.targetHasReceived = Nothing();
129+ this.setState(newState);
109 canGenerate() {
132 canGenerate() {
110 //TODO and canSend()
133 //TODO and canSend()
111− let res = this.state.invitesAvailable.matchWith({
134+ let invites = this.state.invitesAvailable.matchWith({
112 Nothing: () => false,
135 Nothing: () => false,
113− Just: invites => ((invites.value > 0)
136+ Just: invites => (invites.value > 0)
114− && Just.hasInstance(this.state.randomPlanet)
115− && Just.hasInstance(this.state.inviteWallet))
138+ let alreadyGotInvited = this.state.targetHasReceived.matchWith({
139+ Nothing: () => false,
140+ Just: has => has.value
117− return res;
142+ return invites
143+ && !alreadyGotInvited
144+ && Just.hasInstance(this.state.randomPlanet)
145+ && Just.hasInstance(this.state.inviteWallet);
120 createUnsignedTxn() {
148 createUnsignedTxn() {
@@ -127,15 +155,13 @@ class InvitesSend extends React.Component {
127 return Just(txn)
155 return Just(txn)
130− //TODO find a better way of making the user wait for submission success
158+ async beforeSend(stx) {
131− // and email sending, so that we can catch failure cases and can
159+ const rawTx = hexify(stx.serialize());
132− // always fall back to retrying or "please send them this"
160+ return await sendMail(
133− txnConfirmation(txHash, confirmations) {
161+ this.state.targetEmail,
134− this.props.setTxnConfirmations(txHash, confirmations);
135− if (confirmations === 1) {
136− sendMail(this.state.targetEmail, this.state.inviteWallet.value.ticket)
162+ this.state.inviteWallet.value.ticket,
137− .then(console.log);
163+ rawTx
141 render() {
167 render() {
@@ -163,6 +189,19 @@ class InvitesSend extends React.Component {
192+ const recipientWarning = this.state.targetHasReceived.matchWith({
193+ Nothing: () => null,
194+ Just: hasReceived => {
195+ if (!hasReceived.value) return null;
198+ <h3 className={'mb-2'}>{'Warning'}</h3>
199+ { 'That email address has already received an invite!' }
@@ -181,6 +220,8 @@ class InvitesSend extends React.Component {
181 </InnerLabel>
220 </InnerLabel>
223+ { recipientWarning }
184 <StatelessTransaction
225 <StatelessTransaction
185 // Upper scope
226 // Upper scope
186 web3={this.props.web3}
227 web3={this.props.web3}
@@ -189,8 +230,9 @@ class InvitesSend extends React.Component {
189 walletType={this.props.walletType}
230 walletType={this.props.walletType}
190 walletHdPath={this.props.walletHdPath}
231 walletHdPath={this.props.walletHdPath}
191 networkType={this.props.networkType}
232 networkType={this.props.networkType}
192− setTxnConfirmations={this.txnConfirmation}
233+ beforeSend={this.beforeSend}
193 onSent={this.props.setTxnHashCursor}
234 onSent={this.props.setTxnHashCursor}
235+ setTxnConfirmations={this.props.setTxnConfirmations}
194 popRoute={this.props.popRoute}
236 popRoute={this.props.popRoute}
195 pushRoute={this.props.pushRoute}
237 pushRoute={this.props.pushRoute}