2015-12-25 22:42:14 +01:00
/ *
This file is part of TALER
( C ) 2015 GNUnet e . V .
TALER is free software ; you can redistribute it and / or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation ; either version 3 , or ( at your option ) any later version .
TALER is distributed in the hope that it will be useful , but WITHOUT ANY
WARRANTY ; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE . See the GNU General Public License for more details .
You should have received a copy of the GNU General Public License along with
2016-07-07 17:59:29 +02:00
TALER ; see the file COPYING . If not , see < http : / / www.gnu.org / licenses / >
2015-12-25 22:42:14 +01:00
* /
2016-03-01 19:46:20 +01:00
/ * *
* Page shown to the user to confirm entering
* a contract .
* /
2016-11-13 23:30:18 +01:00
2017-05-24 16:14:23 +02:00
/ * *
* Imports .
* /
2017-05-24 16:45:57 +02:00
import { Contract , AmountJson , ExchangeRecord } from "../types" ;
import { OfferRecord } from "../wallet" ;
import { renderContract } from "../renderHtml" ;
import { getExchanges } from "../wxApi" ;
2017-04-20 03:09:25 +02:00
import * as i18n from "../i18n" ;
import * as React from "react" ;
import * as ReactDOM from "react-dom" ;
import URI = require ( "urijs" ) ;
2015-12-16 10:45:16 +01:00
2016-10-05 17:04:57 +02:00
interface DetailState {
collapsed : boolean ;
}
interface DetailProps {
2016-10-19 20:16:01 +02:00
contract : Contract
collapsed : boolean
2016-11-15 15:07:17 +01:00
exchanges : null | ExchangeRecord [ ] ;
2016-10-05 17:04:57 +02:00
}
2016-11-13 08:16:12 +01:00
class Details extends React . Component < DetailProps , DetailState > {
2016-10-19 20:16:01 +02:00
constructor ( props : DetailProps ) {
super ( props ) ;
2016-11-13 10:17:39 +01:00
console . log ( "new Details component created" ) ;
this . state = {
2016-10-19 20:16:01 +02:00
collapsed : props.collapsed ,
2016-11-13 10:17:39 +01:00
} ;
2016-10-19 23:27:46 +02:00
console . log ( "initial state:" , this . state ) ;
2016-10-05 17:04:57 +02:00
}
2016-11-13 08:16:12 +01:00
render() {
if ( this . state . collapsed ) {
2016-10-05 17:38:02 +02:00
return (
< div >
< button className = "linky"
2016-10-19 20:16:01 +02:00
onClick = { ( ) = > { this . setState ( { collapsed : false } as any ) } } >
2016-11-23 01:14:45 +01:00
< i18n.Translate wrap = "span" >
2016-10-05 17:38:02 +02:00
show more details
2016-11-23 01:14:45 +01:00
< / i18n.Translate >
2016-10-05 17:38:02 +02:00
< / button >
< / div >
) ;
2016-03-02 02:16:18 +01:00
} else {
2016-10-07 17:10:22 +02:00
return (
< div >
< button className = "linky"
2016-10-19 20:16:01 +02:00
onClick = { ( ) = > this . setState ( { collapsed : true } as any ) } >
2016-10-07 17:10:22 +02:00
show less details
< / button >
< div >
2016-11-27 22:13:24 +01:00
{ i18n . str ` Accepted exchanges: ` }
2016-10-07 17:10:22 +02:00
< ul >
2016-11-13 08:16:12 +01:00
{ this . props . contract . exchanges . map (
2016-10-07 17:10:22 +02:00
e = > < li > { ` ${ e . url } : ${ e . master_pub } ` } < / li > ) }
< / ul >
2016-11-27 22:13:24 +01:00
{ i18n . str ` Exchanges in the wallet: ` }
2016-10-19 20:16:01 +02:00
< ul >
2016-11-14 03:54:51 +01:00
{ ( this . props . exchanges || [ ] ) . map (
2016-11-15 15:07:17 +01:00
( e : ExchangeRecord ) = >
2016-10-19 20:16:01 +02:00
< li > { ` ${ e . baseUrl } : ${ e . masterPublicKey } ` } < / li > ) }
< / ul >
2016-10-07 17:10:22 +02:00
< / div >
< / div > ) ;
2016-03-02 02:16:18 +01:00
}
}
2016-10-05 17:04:57 +02:00
}
2016-03-02 02:16:18 +01:00
2016-10-05 17:04:57 +02:00
interface ContractPromptProps {
2016-11-13 10:17:39 +01:00
offerId : number ;
2016-10-05 17:04:57 +02:00
}
interface ContractPromptState {
2016-11-15 15:07:17 +01:00
offer : OfferRecord | null ;
2016-10-05 17:04:57 +02:00
error : string | null ;
payDisabled : boolean ;
2016-11-15 15:07:17 +01:00
exchanges : null | ExchangeRecord [ ] ;
2016-10-05 17:04:57 +02:00
}
2016-11-13 08:16:12 +01:00
class ContractPrompt extends React . Component < ContractPromptProps , ContractPromptState > {
2016-10-05 17:04:57 +02:00
constructor ( ) {
super ( ) ;
this . state = {
2016-11-14 03:54:51 +01:00
offer : null ,
2016-10-05 17:04:57 +02:00
error : null ,
payDisabled : true ,
2016-11-14 03:54:51 +01:00
exchanges : null
2016-01-26 17:21:17 +01:00
}
2016-10-05 17:04:57 +02:00
}
2016-01-26 17:21:17 +01:00
2016-10-05 17:04:57 +02:00
componentWillMount() {
2016-11-13 10:17:39 +01:00
this . update ( ) ;
2016-10-05 17:04:57 +02:00
}
componentWillUnmount() {
// FIXME: abort running ops
}
2016-01-26 17:21:17 +01:00
2016-11-13 10:17:39 +01:00
async update() {
let offer = await this . getOffer ( ) ;
this . setState ( { offer } as any ) ;
this . checkPayment ( ) ;
2016-11-14 03:54:51 +01:00
let exchanges = await getExchanges ( ) ;
this . setState ( { exchanges } as any ) ;
2016-11-13 10:17:39 +01:00
}
2016-11-15 15:07:17 +01:00
getOffer ( ) : Promise < OfferRecord > {
2017-04-20 03:09:25 +02:00
return new Promise < OfferRecord > ( ( resolve , reject ) = > {
2016-11-13 10:17:39 +01:00
let msg = {
type : 'get-offer' ,
detail : {
offerId : this.props.offerId
}
} ;
chrome . runtime . sendMessage ( msg , ( resp ) = > {
resolve ( resp ) ;
} ) ;
} )
}
2016-10-05 17:04:57 +02:00
checkPayment() {
let msg = {
type : 'check-pay' ,
detail : {
2016-11-13 10:17:39 +01:00
offer : this.state.offer
2016-10-05 17:04:57 +02:00
}
} ;
chrome . runtime . sendMessage ( msg , ( resp ) = > {
2016-04-27 06:03:04 +02:00
if ( resp . error ) {
console . log ( "check-pay error" , JSON . stringify ( resp ) ) ;
switch ( resp . error ) {
case "coins-insufficient" :
2016-11-27 22:13:24 +01:00
let msgInsufficient = i18n . str ` You have insufficient funds of the requested currency in your wallet. ` ;
let msgNoMatch = i18n . str ` You do not have any funds from an exchange that is accepted by this merchant. None of the exchanges accepted by the merchant is known to your wallet. ` ;
2016-11-14 03:54:51 +01:00
if ( this . state . exchanges && this . state . offer ) {
let acceptedExchangePubs = this . state . offer . contract . exchanges . map ( ( e ) = > e . master_pub ) ;
let ex = this . state . exchanges . find ( ( e ) = > acceptedExchangePubs . indexOf ( e . masterPublicKey ) >= 0 ) ;
if ( ex ) {
2017-04-20 03:09:25 +02:00
this . setState ( { error : msgInsufficient } ) ;
2016-11-14 03:54:51 +01:00
} else {
2017-04-20 03:09:25 +02:00
this . setState ( { error : msgNoMatch } ) ;
2016-11-14 03:54:51 +01:00
}
} else {
2017-04-20 03:09:25 +02:00
this . setState ( { error : msgInsufficient } ) ;
2016-11-14 03:54:51 +01:00
}
2016-04-27 06:03:04 +02:00
break ;
default :
2017-04-20 03:09:25 +02:00
this . setState ( { error : ` Error: ${ resp . error } ` } ) ;
2016-04-27 06:03:04 +02:00
break ;
}
2017-04-20 03:09:25 +02:00
this . setState ( { payDisabled : true } ) ;
2016-04-27 06:03:04 +02:00
} else {
2017-04-20 03:09:25 +02:00
this . setState ( { payDisabled : false , error : null } ) ;
2016-04-27 06:03:04 +02:00
}
2016-10-19 23:27:46 +02:00
this . setState ( { } as any ) ;
2016-11-13 10:17:39 +01:00
window . setTimeout ( ( ) = > this . checkPayment ( ) , 500 ) ;
2016-04-27 06:03:04 +02:00
} ) ;
}
2016-10-05 17:04:57 +02:00
doPayment() {
2016-11-13 10:17:39 +01:00
let d = { offer : this.state.offer } ;
2016-01-26 17:21:17 +01:00
chrome . runtime . sendMessage ( { type : 'confirm-pay' , detail : d } , ( resp ) = > {
2016-02-15 15:53:59 +01:00
if ( resp . error ) {
2016-01-26 17:21:17 +01:00
console . log ( "confirm-pay error" , JSON . stringify ( resp ) ) ;
2016-02-15 15:53:59 +01:00
switch ( resp . error ) {
case "coins-insufficient" :
2017-04-20 03:09:25 +02:00
this . setState ( { error : "You do not have enough coins of the requested currency." } ) ;
2016-02-15 15:53:59 +01:00
break ;
default :
2017-04-20 03:09:25 +02:00
this . setState ( { error : ` Error: ${ resp . error } ` } ) ;
2016-02-15 15:53:59 +01:00
break ;
}
2015-12-17 22:56:24 +01:00
return ;
2015-12-16 10:45:16 +01:00
}
2016-11-14 03:54:51 +01:00
let c = d . offer ! . contract ;
2016-01-26 17:21:17 +01:00
console . log ( "contract" , c ) ;
2017-05-24 16:14:23 +02:00
document . location . href = c . fulfillment_url ;
2015-12-16 10:45:16 +01:00
} ) ;
2016-01-26 17:21:17 +01:00
}
2016-10-05 17:04:57 +02:00
2016-11-13 08:16:12 +01:00
render() {
2016-11-13 10:17:39 +01:00
if ( ! this . state . offer ) {
return < span > . . . < / span > ;
}
let c = this . state . offer . contract ;
2016-10-07 17:10:22 +02:00
return (
< div >
2016-11-13 10:17:39 +01:00
< div >
{ renderContract ( c ) }
< / div >
2016-10-07 17:10:22 +02:00
< button onClick = { ( ) = > this . doPayment ( ) }
2016-11-13 08:16:12 +01:00
disabled = { this . state . payDisabled }
2016-10-07 17:10:22 +02:00
className = "accept" >
Confirm payment
< / button >
2016-11-13 10:17:39 +01:00
< div >
{ ( this . state . error ? < p className = "errorbox" > { this . state . error } < / p > : < p / > ) }
< / div >
2016-11-14 03:54:51 +01:00
< Details exchanges = { this . state . exchanges } contract = { c } collapsed = { ! this . state . error } / >
2016-10-07 17:10:22 +02:00
< / div >
2016-10-05 17:04:57 +02:00
) ;
}
}
2017-04-20 03:09:25 +02:00
document . addEventListener ( "DOMContentLoaded" , ( ) = > {
let url = new URI ( document . location . href ) ;
2016-10-05 17:04:57 +02:00
let query : any = URI . parseQuery ( url . query ( ) ) ;
2016-11-13 10:17:39 +01:00
let offerId = JSON . parse ( query . offerId ) ;
2016-10-05 17:04:57 +02:00
2016-11-13 10:17:39 +01:00
ReactDOM . render ( < ContractPrompt offerId = { offerId } / > , document . getElementById (
2016-10-07 17:10:22 +02:00
"contract" ) ! ) ;
2017-04-20 03:09:25 +02:00
} ) ;