on master help sign in
Send invite email on transaction confirmation
7029bf4Fang
src/bridge/lib/inviteMail.jsnew file+27
@@ -0,0 +1,27 @@
1+//NOTE if accessing this in a localhost configuration fails with "CORS request
2+// did not succeed", you might need to visit localhost:3001 or whatever
3+// explicitly and tell your browser that's safe to access.
4+// https://stackoverflow.com/a/53011185/1334324
5+const baseUrl = 'https://localhost:3002'
6+
7+function sendMail(recipient, ticket) {
8+ return new Promise((resolve, reject) => {
9+ fetch(baseUrl + '/send-ticket', {
10+ method: "POST",
11+ // mode: "cors", // no-cors, cors, *same-origin
12+ cache: "no-cache", // *default, no-cache, reload, force-cache, only-if-cached
13+ // credentials: "omit", // include, *same-origin, omit
14+ headers: {
15+ "Content-Type": "application/json",
16+ },
17+ body: JSON.stringify({recipient,ticket}),
18+ })
19+ .then(response => resolve(response.ok))
20+ .catch(reject);
21+ });
22+}
23+
24+export {
25+ sendMail
26+}
27+
src/bridge/views/InvitesSend.js+48−8
@@ -16,7 +16,10 @@ import ReactSVGComponents from '../components/ReactSVGComponents'
16import KeysAndMetadata from './Point/KeysAndMetadata'
16import KeysAndMetadata from './Point/KeysAndMetadata'
17import Actions from './Point/Actions'
17import Actions from './Point/Actions'
18import { BRIDGE_ERROR } from '../lib/error'
18import { BRIDGE_ERROR } from '../lib/error'
19+import { sendMail } from '../lib/inviteMail'
19−import { Row, Col, Warning, Button, H1, H3 } from '../components/Base'
20+import { Row, Col, Warning, Button, H1, H3,
21+ Input, InnerLabel
22+} from '../components/Base'
20import StatelessTransaction from '../components/StatelessTransaction'
23import StatelessTransaction from '../components/StatelessTransaction'
21
24
22// for wallet generation
25// for wallet generation
@@ -35,7 +38,6 @@ import {
35import * as tank from '../lib/tank'
38import * as tank from '../lib/tank'
36import Tx from 'ethereumjs-tx'
39import Tx from 'ethereumjs-tx'
37
40
38−//TODO needs more work to include email sending in the process
39class InvitesSend extends React.Component {
41class InvitesSend extends React.Component {
40
42
41 constructor(props) {
43 constructor(props) {
@@ -46,13 +48,19 @@ class InvitesSend extends React.Component {
46 fromPool: Nothing(),
48 fromPool: Nothing(),
47 haveInvited: Nothing(),
49 haveInvited: Nothing(),
48 randomPlanet: Nothing(),
50 randomPlanet: Nothing(),
49− inviteWallet: Nothing()
51+ inviteWallet: Nothing(),
52+ targetEmail: ''
50 }
53 }
51
54
52 this.findInvited = this.findInvited.bind(this);
55 this.findInvited = this.findInvited.bind(this);
53 this.findRandomPlanet = this.findRandomPlanet.bind(this);
56 this.findRandomPlanet = this.findRandomPlanet.bind(this);
54 this.generateWallet = this.generateWallet.bind(this);
57 this.generateWallet = this.generateWallet.bind(this);
55 this.canGenerate = this.canGenerate.bind(this);
58 this.canGenerate = this.canGenerate.bind(this);
59+ this.handleEmailInput = this.handleEmailInput.bind(this);
60+ this.txnConfirmation = this.txnConfirmation.bind(this);
61+ this.createUnsignedTxn = this.createUnsignedTxn.bind(this);
62+
63+ this.promptText = 'The email will be sent on first confirmation. Please wait.';
56 }
64 }
57
65
58 componentDidMount() {
66 componentDidMount() {
@@ -91,10 +99,11 @@ class InvitesSend extends React.Component {
91 this.contracts,
99 this.contracts,
92 this.point
100 this.point
93 );
101 );
94− invited.map(async point => {
102+ invited = invited.map(async point => {
95 const active = await azimuth.azimuth.isActive(this.contracts, point);
103 const active = await azimuth.azimuth.isActive(this.contracts, point);
96 console.log('point active', point, active);
104 console.log('point active', point, active);
97− return {point: Number(point), active};
105+ const res = {point: Number(point), active:active};
106+ return res;
98 });
107 });
99 invited = await Promise.all(invited);
108 invited = await Promise.all(invited);
100 this.setState({haveInvited: Just(invited)});
109 this.setState({haveInvited: Just(invited)});
@@ -148,9 +157,13 @@ class InvitesSend extends React.Component {
148 });
157 });
149 }
158 }
150
159
160+ handleEmailInput(email) {
161+ //TODO simple .*@.*\..* email validation
162+ this.setState({ targetEmail: email });
163+ }
164+
151 canGenerate() {
165 canGenerate() {
152 //TODO and canSend()
166 //TODO and canSend()
153− console.log('can generate', this.state.invitesAvailable, this.state.randomPlanet,);
154 let res = this.state.invitesAvailable.matchWith({
167 let res = this.state.invitesAvailable.matchWith({
155 Nothing: () => false,
168 Nothing: () => false,
156 Just: invites => ((invites.value > 0)
169 Just: invites => ((invites.value > 0)
@@ -160,6 +173,24 @@ class InvitesSend extends React.Component {
160 return res;
173 return res;
161 }
174 }
162
175
176+ createUnsignedTxn() {
177+ const txn = azimuth.delegatedSending.sendPoint(
178+ this.contracts,
179+ this.point,
180+ this.state.randomPlanet.value,
181+ this.state.inviteWallet.value.owner
182+ );
183+ return Just(txn)
184+ }
185+
186+ txnConfirmation(txHash, confirmations) {
187+ this.props.setTxnConfirmations(txHash, confirmations);
188+ if (confirmations === 1) {
189+ sendMail(this.state.targetEmail, this.state.inviteWallet.value.ticket)
190+ .then(console.log);
191+ }
192+ }
193+
163 render() {
194 render() {
164
195
165 const inviteStatus = this.state.invitesAvailable.matchWith({
196 const inviteStatus = this.state.invitesAvailable.matchWith({
@@ -180,7 +211,7 @@ class InvitesSend extends React.Component {
180 Nothing: () => (<li>{'Loading...'}</li>),
211 Nothing: () => (<li>{'Loading...'}</li>),
181 Just: (invited) => invited.value.map(i => {
212 Just: (invited) => invited.value.map(i => {
182 return (<li>
213 return (<li>
183− <span class="invitee">{ob.patp(i.point)}</span>
214+ <span class="invitee">{ob.patp(i.point)}</span>:
184 <span class="status">{i.active ? 'accepted' : 'pending'}</span>
215 <span class="status">{i.active ? 'accepted' : 'pending'}</span>
185 </li>);
216 </li>);
186 })
217 })
@@ -196,6 +227,14 @@ class InvitesSend extends React.Component {
196
227
197 <ul>{ inviteList }</ul>
228 <ul>{ inviteList }</ul>
198
229
230+ <Input
231+ value={ this.state.targetEmail }
232+ onChange={ this.handleEmailInput }>
233+ <InnerLabel>
234+ { 'Recipient email' }
235+ </InnerLabel>
236+ </Input>
237+
199 <StatelessTransaction
238 <StatelessTransaction
200 // Upper scope
239 // Upper scope
201 web3={this.props.web3}
240 web3={this.props.web3}
@@ -205,10 +244,11 @@ class InvitesSend extends React.Component {
205 walletHdPath={this.props.walletHdPath}
244 walletHdPath={this.props.walletHdPath}
206 networkType={this.props.networkType}
245 networkType={this.props.networkType}
207 setTxnHashCursor={this.props.setTxnHashCursor}
246 setTxnHashCursor={this.props.setTxnHashCursor}
208− setTxnConfirmations={this.props.setTxnConfirmations}
247+ setTxnConfirmations={this.txnConfirmation}
209 popRoute={this.props.popRoute}
248 popRoute={this.props.popRoute}
210 pushRoute={this.props.pushRoute}
249 pushRoute={this.props.pushRoute}
211 // Other
250 // Other
251+ promptText={this.promptText}
212 canGenerate={this.canGenerate()}
252 canGenerate={this.canGenerate()}
213 createUnsignedTxn={this.createUnsignedTxn}
253 createUnsignedTxn={this.createUnsignedTxn}
214 ref={this.statelessRef} />
254 ref={this.statelessRef} />