@@ -3,7 +3,7 @@ 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 , ethErrorHandler , fromWei } from "lib/util" ;
6
+ import { getArcByDAOAddress , standardPolling , getNetworkByDAOAddress , toWei , ethErrorHandler , fromWei , formatTokens } from "lib/util" ;
7
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" ;
@@ -18,7 +18,7 @@ import { lock, releaseLocking, extendLocking, redeemLocking, approveTokens } fro
18
18
import { showNotification } from "@store/notifications/notifications.reducer" ;
19
19
import { connect } from "react-redux" ;
20
20
import Tooltip from "rc-tooltip" ;
21
- import { getCL4RParams , ICL4RParams } from "./CL4RHelper" ;
21
+ import { calculateTotalRedeemedAmount , getCL4RParams , ICL4RParams , secondsToDays } from "./CL4RHelper" ;
22
22
import BN from "bn.js" ;
23
23
24
24
interface IDispatchProps {
@@ -58,27 +58,25 @@ const CL4R = (props: IProps) => {
58
58
const [ cl4rScheme , setCL4RScheme ] = React . useState < CL4RScheme > ( ) ;
59
59
const [ isLocking , setIsLocking ] = React . useState ( false ) ;
60
60
const [ isApprovingToken , setIsApprovingToken ] = React . useState ( false ) ;
61
+ const [ isRedeeming , setIsRedeeming ] = React . useState ( false ) ;
61
62
const [ currentTime , setCurrentTime ] = React . useState ( moment ( ) . unix ( ) ) ;
62
63
const isAllowance = data [ 1 ] ?. gt ( new BN ( 0 ) ) ;
63
64
const isEnoughBalance = fromWei ( data [ 2 ] ) >= lockAmount ;
64
65
const cl4Rlocks = ( data as any ) [ 0 ] . data . cl4Rlocks ;
65
-
66
- const getLockingBatch = React . useCallback ( ( lockingTime : number , startTime : number , batchTime : number ) : number => {
67
- const timeElapsed = lockingTime - startTime ;
68
- return Math . trunc ( timeElapsed / batchTime ) ;
69
- } , [ schemeParams ] ) ;
66
+ const [ redeemableAmount , setRedeemableAmount ] = React . useState ( 0 ) ;
67
+ const [ allLockingIdsForRedeem , setAllLockingIdsForRedeem ] = React . useState < Set < number > > ( new Set ( ) ) ;
70
68
71
69
const isLockingStarted = React . useMemo ( ( ) => {
72
70
return ( moment ( ) . isAfter ( moment . unix ( Number ( schemeParams . startTime ) ) ) ) ;
73
71
} , [ schemeParams ] ) ;
74
72
75
73
const endTime = React . useMemo ( ( ) => {
76
- return Number ( schemeParams . startTime ) + ( Number ( schemeParams . batchTime ) * Number ( schemeParams . batchesIndexCap ) ) ;
74
+ return Number ( schemeParams . startTime ) + ( schemeParams . batchTime * schemeParams . batchesIndexCap ) ;
77
75
} , [ schemeParams ] ) ;
78
76
79
77
const isLockingEnded = React . useMemo ( ( ) => {
80
78
return ( moment ( ) . isAfter ( moment . unix ( endTime ) ) ) ;
81
- } , [ schemeParams ] ) ;
79
+ } , [ schemeParams , currentTime ] ) ;
82
80
83
81
const handleRelease = React . useCallback ( async ( lockingId : number , setIsReleasing : any ) => {
84
82
if ( ! await enableWalletProvider ( { showNotification : props . showNotification } , getNetworkByDAOAddress ( daoState . address ) ) ) { return ; }
@@ -90,9 +88,9 @@ const CL4R = (props: IProps) => {
90
88
props . extendLocking ( cl4rScheme , extendPeriod , batchIndexToLockIn , lockingId , schemeParams . agreementHash , setIsExtending ) ;
91
89
} , [ cl4rScheme , schemeParams ] ) ;
92
90
93
- const handleRedeem = React . useCallback ( async ( lockingId : number [ ] , setIsRedeeming : any ) => {
91
+ const handleRedeem = React . useCallback ( async ( lockingIds : number [ ] , setIsRedeeming : any ) => {
94
92
if ( ! await enableWalletProvider ( { showNotification : props . showNotification } , getNetworkByDAOAddress ( daoState . address ) ) ) { return ; }
95
- props . redeemLocking ( cl4rScheme , props . currentAccountAddress , lockingId , setIsRedeeming ) ;
93
+ props . redeemLocking ( cl4rScheme , props . currentAccountAddress , lockingIds , setIsRedeeming ) ;
96
94
} , [ cl4rScheme , schemeParams ] ) ;
97
95
98
96
const handleTokenApproving = React . useCallback ( async ( ) => {
@@ -111,17 +109,22 @@ const CL4R = (props: IProps) => {
111
109
getSchemeInfo ( ) ;
112
110
} , [ ] ) ;
113
111
112
+ const redeemedAmount = React . useMemo ( ( ) => {
113
+ return calculateTotalRedeemedAmount ( cl4Rlocks ) ;
114
+ } , [ schemeParams , cl4Rlocks ] ) ;
115
+
114
116
const durations = [ ] as any ;
115
- for ( let duration = 1 ; duration <= Number ( schemeParams . maxLockingBatches ) ; duration ++ ) {
116
- if ( moment ( ) . unix ( ) + ( duration * Number ( schemeParams . batchTime ) ) <= endTime ) {
117
+ for ( let duration = 1 ; duration <= schemeParams . maxLockingBatches ; duration ++ ) {
118
+ if ( moment ( ) . unix ( ) + ( duration * schemeParams . batchTime ) <= endTime ) {
117
119
durations . push ( < option key = { duration } value = { duration } selected = { duration === 1 } > { duration } </ option > ) ;
118
120
}
119
121
}
120
122
121
123
const startTime = Number ( schemeParams . startTime ) ;
122
124
const timeElapsed = currentTime - startTime ;
123
- const currentLockingBatch = isLockingEnded ? Number ( schemeParams . batchesIndexCap ) : Math . trunc ( timeElapsed / Number ( schemeParams . batchTime ) ) ;
124
- const nextBatchStartTime = moment . unix ( startTime + ( ( currentLockingBatch + 1 ) * Number ( schemeParams . batchTime ) ) ) ;
125
+ const currentLockingBatch = isLockingEnded ? schemeParams . batchesIndexCap : Math . trunc ( timeElapsed / schemeParams . batchTime ) ;
126
+ const nextBatchStartTime = moment . unix ( startTime + ( ( currentLockingBatch + 1 ) * schemeParams . batchTime ) ) ;
127
+ const redeemable = moment ( ) . isSameOrAfter ( moment . unix ( Number ( schemeParams . redeemEnableTime ) ) ) ;
125
128
126
129
const handleLock = React . useCallback ( async ( ) => {
127
130
if ( ! await enableWalletProvider ( { showNotification : props . showNotification } , getNetworkByDAOAddress ( daoState . address ) ) ) { return ; }
@@ -138,10 +141,11 @@ const CL4R = (props: IProps) => {
138
141
cl4rScheme = { cl4rScheme }
139
142
currentLockingBatch = { currentLockingBatch }
140
143
isLockingEnded = { isLockingEnded }
141
- getLockingBatch = { getLockingBatch }
142
- handleRedeem = { handleRedeem } /> ) ;
144
+ redeemableAmount = { redeemableAmount }
145
+ setRedeemableAmount = { setRedeemableAmount }
146
+ allLockingIdsForRedeem = { allLockingIdsForRedeem }
147
+ setAllLockingIdsForRedeem = { setAllLockingIdsForRedeem } /> ) ;
143
148
}
144
-
145
149
if ( isLockingEnded ) {
146
150
periods . splice ( - 1 , 1 ) ;
147
151
}
@@ -155,7 +159,6 @@ const CL4R = (props: IProps) => {
155
159
lockData = { lock }
156
160
handleRelease = { handleRelease }
157
161
handleExtend = { handleExtend }
158
- getLockingBatch = { getLockingBatch }
159
162
endTime = { endTime }
160
163
currentLockingBatch = { currentLockingBatch }
161
164
isLockingEnded = { isLockingEnded } /> ;
@@ -167,7 +170,7 @@ const CL4R = (props: IProps) => {
167
170
prefix = "Starts in" ;
168
171
}
169
172
170
- if ( currentLockingBatch + 1 === Number ( schemeParams . batchesIndexCap ) ) {
173
+ if ( currentLockingBatch + 1 === schemeParams . batchesIndexCap ) {
171
174
prefix = "Ends in" ;
172
175
}
173
176
@@ -182,22 +185,38 @@ const CL4R = (props: IProps) => {
182
185
} ) ;
183
186
184
187
const lockButtonClass = classNames ( {
185
- [ css . lockButton ] : true ,
188
+ [ css . actionButton ] : true ,
186
189
[ css . disabled ] : ! lockAmount || ! lockDuration || isLocking || ! isEnoughBalance ,
187
190
} ) ;
188
191
189
192
const approveTokenButtonClass = classNames ( {
190
- [ css . lockButton ] : true ,
193
+ [ css . actionButton ] : true ,
191
194
[ css . disabled ] : isApprovingToken ,
192
195
} ) ;
193
196
197
+ const actionButtonClass = classNames ( {
198
+ [ css . actionButton ] : true ,
199
+ [ css . disabled ] : isRedeeming || ! redeemable || redeemableAmount === 0 ,
200
+ } ) ;
201
+
194
202
return (
195
203
! loading ? < div className = { css . wrapper } >
196
204
< div className = { css . leftWrapper } >
197
- < div className = { css . currentPeriod } > Current Period: { isLockingEnded ? Number ( schemeParams . batchesIndexCap ) : currentLockingBatch + 1 } of { schemeParams . batchesIndexCap } </ div >
198
- < div className = { css . nextPeriod } > { isLockingEnded ? "Locking Ended" : < div > { prefix } < Countdown toDate = { nextBatchStartTime } onEnd = { ( ) => setCurrentTime ( moment ( ) . unix ( ) ) } /> </ div > } </ div >
205
+ < div className = { css . top } >
206
+ < div className = { css . countersWrapper } >
207
+ < div className = { css . currentPeriod } > Current Period: { isLockingEnded ? schemeParams . batchesIndexCap : currentLockingBatch + 1 } of { schemeParams . batchesIndexCap } </ div >
208
+ < div className = { css . nextPeriod } > { isLockingEnded ? "Locking Ended" : < div > { prefix } < Countdown toDate = { nextBatchStartTime } onEnd = { ( ) => setCurrentTime ( moment ( ) . unix ( ) ) } /> </ div > } </ div >
209
+ </ div >
210
+ < div className = { css . redeemWrapper } >
211
+ < button
212
+ className = { actionButtonClass }
213
+ onClick = { ( ) => handleRedeem ( Array . from ( allLockingIdsForRedeem ) , setIsRedeeming ) }
214
+ disabled = { isRedeeming || ! redeemable || redeemableAmount === 0 } > { `Redeem ${ formatTokens ( toWei ( redeemableAmount ) ) } REP` } </ button >
215
+ < div className = { css . redeemedAmountLabel } > { `Total Redeemed: ${ formatTokens ( redeemedAmount , "REP" ) } ` } </ div >
216
+ </ div >
217
+ </ div >
199
218
< div className = { css . tableTitleWrapper } >
200
- < div className = { periodsClass } onClick = { ( ) => setShowYourLocks ( false ) } > All Periods</ div >
219
+ < div className = { periodsClass } onClick = { ( ) => { setShowYourLocks ( false ) ; setRedeemableAmount ( 0 ) ; } } > All Periods</ div >
201
220
< div className = { locksClass } onClick = { ( ) => setShowYourLocks ( true ) } > Your Locks</ div >
202
221
</ div >
203
222
{
@@ -224,7 +243,6 @@ const CL4R = (props: IProps) => {
224
243
< th > You Locked</ th >
225
244
< th > Total Reputation</ th >
226
245
< th > You Will Receive</ th >
227
- < th > Action</ th >
228
246
</ tr >
229
247
</ thead >
230
248
< tbody >
@@ -237,15 +255,15 @@ const CL4R = (props: IProps) => {
237
255
< div className = { css . lockTitle } > New Lock</ div >
238
256
< div className = { css . lockDurationLabel } >
239
257
< span style = { { marginRight : "5px" } } > Lock Duration</ span >
240
- < Tooltip trigger = { [ "hover" ] } overlay = { `Period: ${ schemeParams . batchTime } seconds ` } > < img width = "15px" src = "/assets/images/Icon/question-help.svg" /> </ Tooltip >
258
+ < Tooltip trigger = { [ "hover" ] } overlay = { `Period: ${ secondsToDays ( schemeParams . batchTime ) . toFixed ( 2 ) } days ` } > < img width = "15px" src = "/assets/images/Icon/question-help.svg" /> </ Tooltip >
241
259
</ div >
242
260
< select onChange = { ( e : any ) => setLockDuration ( e . target . value ) } disabled = { ! isAllowance } >
243
261
{ durations }
244
262
</ select >
245
263
< span style = { { marginBottom : "5px" } } > Lock Amount ({ schemeParams . tokenSymbol } )</ span >
246
264
< input type = "number" onChange = { ( e : any ) => setLockAmount ( e . target . value ) } disabled = { ! isAllowance } />
247
265
{ ! 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 > }
266
+ { < span className = { css . releasableLable } > Releasable: { moment ( ) . add ( lockDuration * schemeParams . batchTime , "seconds" ) . format ( "DD.MM.YYYY HH:mm" ) } </ span > }
249
267
{ isAllowance && < button onClick = { handleLock } className = { lockButtonClass } disabled = { ! lockAmount || ! lockDuration } > Lock</ button > }
250
268
{ ! isAllowance && < Tooltip trigger = { [ "hover" ] } overlay = { `Upon activation, the smart contract will be authorized to receive up to 100,000 ${ schemeParams . tokenSymbol } ` } >
251
269
< button onClick = { handleTokenApproving } className = { approveTokenButtonClass } disabled = { isApprovingToken } > Enable Locking</ button >
0 commit comments