@@ -3,28 +3,31 @@ import * as css from "./CL4R.scss";
3
3
import gql from "graphql-tag" ;
4
4
import Loading from "components/Shared/Loading" ;
5
5
import withSubscription , { ISubscriptionProps } from "components/Shared/withSubscription" ;
6
- import { getArcByDAOAddress , standardPolling , getNetworkByDAOAddress , toWei } from "lib/util" ;
7
- import { Address , CL4RScheme , IDAOState , ISchemeState } from "@daostack/arc.js" ;
6
+ import { getArcByDAOAddress , standardPolling , getNetworkByDAOAddress , toWei , ethErrorHandler , fromWei } from "lib/util" ;
7
+ import { Address , CL4RScheme , IDAOState , ISchemeState , Token } from "@daostack/arc.js" ;
8
8
import { RouteComponentProps } from "react-router-dom" ;
9
9
import LockRow from "./LockRow" ;
10
10
import PeriodRow from "./PeriodRow" ;
11
11
import * as classNames from "classnames" ;
12
12
import * as moment from "moment" ;
13
13
import Countdown from "components/Shared/Countdown" ;
14
14
import { first } from "rxjs/operators" ;
15
+ import { combineLatest } from "rxjs" ;
15
16
import { enableWalletProvider } from "arc" ;
16
- import { lock , releaseLocking , extendLocking , redeemLocking } from "@store/arc/arcActions" ;
17
+ import { lock , releaseLocking , extendLocking , redeemLocking , approveTokens } from "@store/arc/arcActions" ;
17
18
import { showNotification } from "@store/notifications/notifications.reducer" ;
18
19
import { connect } from "react-redux" ;
19
20
import Tooltip from "rc-tooltip" ;
20
21
import { getCL4RParams , ICL4RParams } from "./CL4RHelper" ;
22
+ import BN from "bn.js" ;
21
23
22
24
interface IDispatchProps {
23
25
lock : typeof lock ;
24
26
releaseLocking : typeof releaseLocking ;
25
27
extendLocking : typeof extendLocking ;
26
28
redeemLocking : typeof redeemLocking ;
27
29
showNotification : typeof showNotification ;
30
+ approveTokens : typeof approveTokens ;
28
31
}
29
32
30
33
const mapDispatchToProps = {
@@ -33,9 +36,10 @@ const mapDispatchToProps = {
33
36
extendLocking,
34
37
redeemLocking,
35
38
showNotification,
39
+ approveTokens,
36
40
} ;
37
41
38
- type SubscriptionData = Array < any > ;
42
+ type SubscriptionData = [ any , BN , BN ] ;
39
43
type IProps = IExternalProps & ISubscriptionProps < SubscriptionData > & IDispatchProps ;
40
44
type IExternalProps = {
41
45
daoState : IDAOState ;
@@ -50,10 +54,14 @@ const CL4R = (props: IProps) => {
50
54
const [ schemeParams , setSchemeParams ] = React . useState ( { } as ICL4RParams ) ;
51
55
const [ showYourLocks , setShowYourLocks ] = React . useState ( false ) ;
52
56
const [ lockDuration , setLockDuration ] = React . useState ( 1 ) ;
53
- const [ lockAmount , setLockAmount ] = React . useState ( ) ;
57
+ const [ lockAmount , setLockAmount ] = React . useState ( 0 ) ;
54
58
const [ cl4rScheme , setCL4RScheme ] = React . useState < CL4RScheme > ( ) ;
55
59
const [ isLocking , setIsLocking ] = React . useState ( false ) ;
60
+ const [ isApprovingToken , setIsApprovingToken ] = React . useState ( false ) ;
56
61
const [ currentTime , setCurrentTime ] = React . useState ( moment ( ) . unix ( ) ) ;
62
+ const isAllowance = data [ 1 ] . gt ( new BN ( 0 ) ) ;
63
+ const isEnoughBalance = fromWei ( data [ 2 ] ) >= lockAmount ;
64
+ const cl4Rlocks = ( data as any ) [ 0 ] . data . cl4Rlocks ;
57
65
58
66
const getLockingBatch = React . useCallback ( ( lockingTime : number , startTime : number , batchTime : number ) : number => {
59
67
const timeElapsed = lockingTime - startTime ;
@@ -87,6 +95,11 @@ const CL4R = (props: IProps) => {
87
95
props . redeemLocking ( cl4rScheme , props . currentAccountAddress , lockingId , setIsRedeeming ) ;
88
96
} , [ cl4rScheme , schemeParams ] ) ;
89
97
98
+ const handleTokenApproving = React . useCallback ( async ( ) => {
99
+ if ( ! await enableWalletProvider ( { showNotification : props . showNotification } , getNetworkByDAOAddress ( daoState . address ) ) ) { return ; }
100
+ props . approveTokens ( props . scheme . address , getArcByDAOAddress ( daoState . address ) , schemeParams . token , schemeParams . tokenSymbol , setIsApprovingToken ) ;
101
+ } , [ schemeParams ] ) ;
102
+
90
103
React . useEffect ( ( ) => {
91
104
const getSchemeInfo = async ( ) => {
92
105
const arc = getArcByDAOAddress ( daoState . id ) ;
@@ -121,7 +134,7 @@ const CL4R = (props: IProps) => {
121
134
key = { period }
122
135
period = { period }
123
136
schemeParams = { schemeParams }
124
- lockData = { ( data as any ) . data . cl4Rlocks }
137
+ lockData = { cl4Rlocks }
125
138
cl4rScheme = { cl4rScheme }
126
139
currentLockingBatch = { currentLockingBatch }
127
140
isLockingEnded = { isLockingEnded }
@@ -135,7 +148,7 @@ const CL4R = (props: IProps) => {
135
148
136
149
periods . reverse ( ) ;
137
150
138
- const lockings = ( ( data as any ) . data . cl4Rlocks ?. map ( ( lock : any ) => {
151
+ const lockings = ( cl4Rlocks ?. map ( ( lock : any ) => {
139
152
return < LockRow
140
153
key = { lock . id }
141
154
schemeParams = { schemeParams }
@@ -170,7 +183,12 @@ const CL4R = (props: IProps) => {
170
183
171
184
const lockButtonClass = classNames ( {
172
185
[ css . lockButton ] : true ,
173
- [ css . disabled ] : ! lockAmount || ! lockDuration || isLocking ,
186
+ [ css . disabled ] : ! lockAmount || ! lockDuration || isLocking || ! isEnoughBalance ,
187
+ } ) ;
188
+
189
+ const approveTokenButtonClass = classNames ( {
190
+ [ css . lockButton ] : true ,
191
+ [ css . disabled ] : isApprovingToken ,
174
192
} ) ;
175
193
176
194
return (
@@ -183,7 +201,7 @@ const CL4R = (props: IProps) => {
183
201
< div className = { locksClass } onClick = { ( ) => setShowYourLocks ( true ) } > Your Locks</ div >
184
202
</ div >
185
203
{
186
- showYourLocks ? ( data as any ) . data . cl4Rlocks . length > 0 ?
204
+ showYourLocks ? cl4Rlocks . length > 0 ?
187
205
< table >
188
206
< thead >
189
207
< tr >
@@ -218,16 +236,20 @@ const CL4R = (props: IProps) => {
218
236
{ ! isLockingEnded && isLockingStarted && < div className = { css . lockWrapper } >
219
237
< div className = { css . lockTitle } > New Lock</ div >
220
238
< div className = { css . lockDurationLabel } >
221
- < span style = { { marginRight : "5px" } } > Lock Duration</ span >
239
+ < span style = { { marginRight : "5px" } } > Lock Duration</ span >
222
240
< Tooltip trigger = { [ "hover" ] } overlay = { `Period: ${ schemeParams . batchTime } seconds` } > < img width = "15px" src = "/assets/images/Icon/question-help.svg" /> </ Tooltip >
223
241
</ div >
224
- < select onChange = { ( e : any ) => setLockDuration ( e . target . value ) } >
242
+ < select onChange = { ( e : any ) => setLockDuration ( e . target . value ) } disabled = { ! isAllowance } >
225
243
{ durations }
226
244
</ select >
227
245
< span style = { { marginBottom : "5px" } } > Lock Amount ({ schemeParams . tokenSymbol } )</ span >
228
- < input type = "number" onChange = { ( e : any ) => setLockAmount ( e . target . value ) } />
229
- { < span > Releasable: { moment ( ) . add ( lockDuration * Number ( schemeParams . batchTime ) , "seconds" ) . format ( "DD.MM.YYYY HH:mm" ) } </ span > }
230
- < button onClick = { handleLock } className = { lockButtonClass } disabled = { ! lockAmount || ! lockDuration } > Lock</ button >
246
+ < input type = "number" onChange = { ( e : any ) => setLockAmount ( e . target . value ) } disabled = { ! isAllowance } />
247
+ { ! isEnoughBalance && < span className = { css . lowBalanceLabel } > { `Not enough ${ schemeParams . tokenSymbol } !` } </ span > }
248
+ { < span className = { css . releasableLable } > Releasable: { moment ( ) . add ( lockDuration * Number ( schemeParams . batchTime ) , "seconds" ) . format ( "DD.MM.YYYY HH:mm" ) } </ span > }
249
+ { isAllowance && < button onClick = { handleLock } className = { lockButtonClass } disabled = { ! lockAmount || ! lockDuration } > Lock</ button > }
250
+ { ! isAllowance && < Tooltip trigger = { [ "hover" ] } overlay = { `Upon activation, the smart contract will be authorized to receive up to 100,000 ${ schemeParams . tokenSymbol } ` } >
251
+ < button onClick = { handleTokenApproving } className = { approveTokenButtonClass } disabled = { isApprovingToken } > Enable Locking</ button >
252
+ </ Tooltip > }
231
253
</ div > }
232
254
</ div > : < Loading />
233
255
) ;
@@ -241,6 +263,18 @@ const SubscribedCL4R = withSubscription({
241
263
createObservable : async ( props : IProps ) => {
242
264
if ( props . currentAccountAddress ) {
243
265
const arc = getArcByDAOAddress ( props . daoState . id ) ;
266
+ const schemeToken = gql `
267
+ query SchemeInfo {
268
+ controllerSchemes(where: {id: "${ props . scheme . id . toLowerCase ( ) } "}) {
269
+ continuousLocking4ReputationParams {
270
+ id
271
+ token
272
+ }
273
+ }
274
+ }
275
+ ` ;
276
+ const schemeTokenData = await arc . sendQuery ( schemeToken ) ;
277
+ const tokenString = schemeTokenData . data . controllerSchemes [ 0 ] . continuousLocking4ReputationParams . token ;
244
278
const locksQuery = gql `
245
279
query Locks {
246
280
cl4Rlocks(where: {scheme: "${ props . scheme . id . toLowerCase ( ) } ", locker: "${ props . currentAccountAddress } "}) {
@@ -250,15 +284,26 @@ const SubscribedCL4R = withSubscription({
250
284
amount
251
285
lockingTime
252
286
period
253
- redeemed
254
- redeemedAt
255
287
released
256
288
releasedAt
257
- batchIndexRedeemed
289
+ redeemed {
290
+ id
291
+ lock
292
+ amount
293
+ redeemedAt
294
+ batchIndex
295
+ }
258
296
}
259
297
}
260
298
` ;
261
- return arc . getObservable ( locksQuery , standardPolling ( ) ) ;
299
+ const token = new Token ( tokenString , arc ) ;
300
+ const allowance = token . allowance ( props . currentAccountAddress , props . scheme . address ) ;
301
+ const balanceOf = token . balanceOf ( props . currentAccountAddress ) ;
302
+ return combineLatest (
303
+ arc . getObservable ( locksQuery , standardPolling ( ) ) ,
304
+ allowance . pipe ( ethErrorHandler ( ) ) ,
305
+ balanceOf . pipe ( ethErrorHandler ( ) )
306
+ ) ;
262
307
}
263
308
} ,
264
309
} ) ;
0 commit comments