Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
I
interface
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
LuckySwap
interface
Commits
5405648a
Unverified
Commit
5405648a
authored
May 14, 2020
by
Moody Salem
Committed by
GitHub
May 14, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Improve the transaction render style (#767)
parent
4ba7dd95
Changes
8
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
140 additions
and
125 deletions
+140
-125
Transaction.tsx
src/components/AccountDetails/Transaction.tsx
+16
-58
index.tsx
src/components/TxnPopup/index.tsx
+4
-3
index.tsx
src/components/Web3Status/index.tsx
+2
-1
actions.ts
src/state/transactions/actions.ts
+5
-0
hooks.tsx
src/state/transactions/hooks.tsx
+3
-3
reducer.ts
src/state/transactions/reducer.ts
+27
-4
updater.tsx
src/state/transactions/updater.tsx
+52
-46
hooks.ts
src/state/wallet/hooks.ts
+31
-10
No files found.
src/components/AccountDetails/Transaction.tsx
View file @
5405648a
import
React
from
'
react
'
import
styled
,
{
keyframes
}
from
'
styled-components
'
import
styled
from
'
styled-components
'
import
{
Check
,
Triangle
}
from
'
react-feather
'
import
{
useWeb3React
}
from
'
../../hooks
'
import
{
getEtherscanLink
}
from
'
../../utils
'
import
{
Link
,
Spinner
}
from
'
../../theme
'
import
Copy
from
'
./Copy
'
import
Circle
from
'
../../assets/images/circle.svg
'
import
{
transparentize
}
from
'
polished
'
import
{
useAllTransactions
}
from
'
../../state/transactions/hooks
'
const
TransactionStatusWrapper
=
styled
.
div
`
display: flex;
align-items: center;
min-width: 12px;
word-break: break-word;
`
const
TransactionWrapper
=
styled
.
div
`
${({
theme
})
=>
theme
.
flexRowNoWrap
}
justify-content: space-between;
width: 100%;
margin-top: 0.75rem;
a {
min-width: 0;
word-break: break-word;
}
`
const
TransactionStatusText
=
styled
.
span
`
margin-left: 0.5rem;
word-break: keep-all;
const
TransactionStatusText
=
styled
.
div
`
margin-right: 0.5rem;
`
const
rotate
=
keyframes
`
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
`
const
TransactionState
=
styled
.
div
<
{
pending
:
boolean
;
success
?:
boolean
}
>
`
const
TransactionState
=
styled
(
Link
)
<
{
pending
:
boolean
;
success
?:
boolean
}
>
`
display: flex;
background-color:
${({
pending
,
success
,
theme
})
=>
pending
?
transparentize
(
0.95
,
theme
.
primary1
)
:
success
?
transparentize
(
0.95
,
theme
.
green1
)
:
transparentize
(
0.95
,
theme
.
red1
)}
;
border-radius: 1.5rem;
justify-content: space-between;
text-decoration: none !important;
border-radius: 0.5rem;
padding: 0.5rem 0.75rem;
font-weight: 500;
font-size: 0.75rem;
border: 1px solid;
color:
${({
pending
,
success
,
theme
})
=>
(
pending
?
theme
.
primary1
:
success
?
theme
.
green1
:
theme
.
red1
)}
;
border-color:
${({
pending
,
success
,
theme
})
=>
pending
?
transparentize
(
0.75
,
theme
.
primary1
)
...
...
@@ -63,10 +38,6 @@ const TransactionState = styled.div<{ pending: boolean; success?: boolean }>`
?
transparentize
(
0.75
,
theme
.
green1
)
:
transparentize
(
0.75
,
theme
.
red1
)}
;
#pending {
animation: 2s
${
rotate
}
linear infinite;
}
:hover {
border-color:
${({
pending
,
success
,
theme
})
=>
pending
...
...
@@ -76,11 +47,6 @@ const TransactionState = styled.div<{ pending: boolean; success?: boolean }>`
:
transparentize
(
0
,
theme
.
red1
)}
;
}
`
const
ButtonWrapper
=
styled
.
div
<
{
pending
:
boolean
;
success
?:
boolean
}
>
`
a {
color:
${({
pending
,
success
,
theme
})
=>
(
pending
?
theme
.
primary1
:
success
?
theme
.
green1
:
theme
.
red1
)}
;
}
`
export
default
function
Transaction
({
hash
}:
{
hash
:
string
})
{
const
{
chainId
}
=
useWeb3React
()
...
...
@@ -93,19 +59,11 @@ export default function Transaction({ hash }: { hash: string }) {
(
allTransactions
[
hash
].
receipt
.
status
===
1
||
typeof
allTransactions
[
hash
].
receipt
.
status
===
'
undefined
'
)
return
(
<
TransactionWrapper
key=
{
hash
}
>
<
TransactionStatusWrapper
>
<
Link
href=
{
getEtherscanLink
(
chainId
,
hash
,
'
transaction
'
)
}
>
{
summary
?
summary
:
hash
}
↗
</
Link
>
<
Copy
toCopy=
{
hash
}
/>
</
TransactionStatusWrapper
>
<
ButtonWrapper
pending=
{
false
}
success=
{
success
}
>
<
Link
href=
{
getEtherscanLink
(
chainId
,
hash
,
'
transaction
'
)
}
>
<
TransactionState
pending=
{
pending
}
success=
{
success
}
>
{
pending
?
<
Spinner
src=
{
Circle
}
/>
:
success
?
<
Check
size=
"16"
/>
:
<
Triangle
size=
"16"
/>
}
<
TransactionStatusText
>
{
pending
?
'
Pending
'
:
success
?
'
Success
'
:
'
Failed
'
}
</
TransactionStatusText
>
</
TransactionState
>
</
Link
>
</
ButtonWrapper
>
<
TransactionWrapper
>
<
TransactionState
href=
{
getEtherscanLink
(
chainId
,
hash
,
'
transaction
'
)
}
pending=
{
pending
}
success=
{
success
}
>
<
TransactionStatusText
>
{
summary
?
summary
:
hash
}
</
TransactionStatusText
>
{
pending
?
<
Spinner
src=
{
Circle
}
/>
:
success
?
<
Check
size=
"16"
/>
:
<
Triangle
size=
"16"
/>
}
</
TransactionState
>
</
TransactionWrapper
>
)
}
src/components/TxnPopup/index.tsx
View file @
5405648a
import
React
,
{
useState
}
from
'
react
'
import
React
,
{
use
Callback
,
use
State
}
from
'
react
'
import
{
AlertCircle
,
CheckCircle
}
from
'
react-feather
'
...
...
@@ -43,10 +43,11 @@ export default function TxnPopup({
const
[
isRunning
,
setIsRunning
]
=
useState
(
true
)
const
removePopup
=
useRemovePopup
()
const
removeThisPopup
=
useCallback
(()
=>
removePopup
(
popKey
),
[
popKey
,
removePopup
])
useInterval
(
()
=>
{
count
>
150
&&
removePopup
(
popKey
)
setCount
(
count
+
1
)
count
>
150
?
removeThisPopup
()
:
setCount
(
count
+
1
)
},
isRunning
?
delay
:
null
)
...
...
src/components/Web3Status/index.tsx
View file @
5405648a
...
...
@@ -95,7 +95,8 @@ const Web3StatusConnected = styled(Web3StatusGeneric)<{ pending?: boolean }>`
border: 1px solid
${({
pending
,
theme
})
=>
(
pending
?
theme
.
primary1
:
theme
.
bg3
)}
;
color:
${({
pending
,
theme
})
=>
(
pending
?
theme
.
white
:
theme
.
text1
)}
;
font-weight: 500;
:hover {
:hover,
:focus {
background-color:
${({
pending
,
theme
})
=>
(
pending
?
darken
(
0.05
,
theme
.
primary1
)
:
lighten
(
0.05
,
theme
.
bg2
))}
;
:focus {
...
...
src/state/transactions/actions.ts
View file @
5405648a
...
...
@@ -14,6 +14,7 @@ export interface SerializableTransactionReceipt {
export
const
addTransaction
=
createAction
<
{
chainId
:
number
hash
:
string
from
:
string
approvalOfToken
?:
string
summary
?:
string
}
>
(
'
addTransaction
'
)
...
...
@@ -23,3 +24,7 @@ export const finalizeTransaction = createAction<{
hash
:
string
receipt
:
SerializableTransactionReceipt
}
>
(
'
finalizeTransaction
'
)
export
const
updateTransactionCount
=
createAction
<
{
address
:
string
;
transactionCount
:
number
;
chainId
:
number
}
>
(
'
updateTransactionCount
'
)
src/state/transactions/hooks.tsx
View file @
5405648a
...
...
@@ -12,7 +12,7 @@ export function useTransactionAdder(): (
response
:
TransactionResponse
,
customData
?:
{
summary
?:
string
;
approvalOfToken
?:
string
}
)
=>
void
{
const
{
chainId
}
=
useWeb3React
()
const
{
chainId
,
account
}
=
useWeb3React
()
const
dispatch
=
useDispatch
<
AppDispatch
>
()
return
useCallback
(
...
...
@@ -24,9 +24,9 @@ export function useTransactionAdder(): (
if
(
!
hash
)
{
throw
Error
(
'
No transaction hash found.
'
)
}
dispatch
(
addTransaction
({
hash
,
chainId
,
approvalOfToken
,
summary
}))
dispatch
(
addTransaction
({
hash
,
from
:
account
,
chainId
,
approvalOfToken
,
summary
}))
},
[
dispatch
,
chainId
]
[
dispatch
,
chainId
,
account
]
)
}
...
...
src/state/transactions/reducer.ts
View file @
5405648a
import
{
createReducer
}
from
'
@reduxjs/toolkit
'
import
{
addTransaction
,
checkTransaction
,
finalizeTransaction
,
SerializableTransactionReceipt
}
from
'
./actions
'
import
{
isAddress
}
from
'
../../utils
'
import
{
addTransaction
,
checkTransaction
,
finalizeTransaction
,
SerializableTransactionReceipt
,
updateTransactionCount
}
from
'
./actions
'
const
now
=
()
=>
new
Date
().
getTime
()
...
...
@@ -11,6 +18,11 @@ export interface TransactionDetails {
receipt
?:
SerializableTransactionReceipt
addedTime
:
number
confirmedTime
?:
number
from
:
string
nonce
?:
number
// todo: find a way to populate this
// set to true when we receive a transaction count that exceeds the nonce of this transaction
unknownStatus
?:
boolean
}
export
interface
TransactionState
{
...
...
@@ -23,19 +35,19 @@ const initialState: TransactionState = {}
export
default
createReducer
(
initialState
,
builder
=>
builder
.
addCase
(
addTransaction
,
(
state
,
{
payload
:
{
chainId
,
hash
,
approvalOfToken
,
summary
}
})
=>
{
.
addCase
(
addTransaction
,
(
state
,
{
payload
:
{
chainId
,
from
,
hash
,
approvalOfToken
,
summary
}
})
=>
{
if
(
state
[
chainId
]?.[
hash
])
{
throw
Error
(
'
Attempted to add existing transaction.
'
)
}
state
[
chainId
]
=
state
[
chainId
]
??
{}
state
[
chainId
][
hash
]
=
{
hash
,
approvalOfToken
,
summary
,
addedTime
:
now
()
}
state
[
chainId
][
hash
]
=
{
hash
,
approvalOfToken
,
summary
,
from
,
addedTime
:
now
()
}
})
.
addCase
(
checkTransaction
,
(
state
,
{
payload
:
{
chainId
,
blockNumber
,
hash
}
})
=>
{
if
(
!
state
[
chainId
]?.[
hash
])
{
throw
Error
(
'
Attempted to check non-existent transaction.
'
)
}
state
[
chainId
][
hash
].
blockNumberChecked
=
blockNumber
state
[
chainId
][
hash
].
blockNumberChecked
=
Math
.
max
(
blockNumber
??
0
,
state
[
chainId
][
hash
].
blockNumberChecked
??
0
)
})
.
addCase
(
finalizeTransaction
,
(
state
,
{
payload
:
{
hash
,
chainId
,
receipt
}
})
=>
{
if
(
!
state
[
chainId
]?.[
hash
])
{
...
...
@@ -43,6 +55,17 @@ export default createReducer(initialState, builder =>
}
state
[
chainId
]
=
state
[
chainId
]
??
{}
state
[
chainId
][
hash
].
receipt
=
receipt
state
[
chainId
][
hash
].
unknownStatus
=
false
state
[
chainId
][
hash
].
confirmedTime
=
now
()
})
// marks every transaction with a nonce less than the transaction count unknown if it was pending
// this can be overridden by a finalize that comes later
.
addCase
(
updateTransactionCount
,
(
state
,
{
payload
:
{
transactionCount
,
address
,
chainId
}
})
=>
{
// mark any transactions under the transaction count to be unknown status
Object
.
values
(
state
?.[
chainId
]
??
{})
.
filter
(
t
=>
!
t
.
receipt
)
.
filter
(
t
=>
t
.
from
===
isAddress
(
address
))
.
filter
(
t
=>
typeof
t
.
nonce
&&
t
.
nonce
<
transactionCount
)
.
forEach
(
t
=>
(
t
.
unknownStatus
=
t
.
unknownStatus
??
true
))
})
)
src/state/transactions/updater.tsx
View file @
5405648a
...
...
@@ -3,12 +3,13 @@ import { useDispatch, useSelector } from 'react-redux'
import
{
useWeb3React
}
from
'
../../hooks
'
import
{
useAddPopup
,
useBlockNumber
}
from
'
../application/hooks
'
import
{
AppDispatch
,
AppState
}
from
'
../index
'
import
{
checkTransaction
,
finalizeTransaction
}
from
'
./actions
'
import
{
checkTransaction
,
finalizeTransaction
,
updateTransactionCount
}
from
'
./actions
'
import
useSWR
from
'
swr
'
export
default
function
Updater
()
{
const
{
chainId
,
library
}
=
useWeb3React
()
const
{
chainId
,
account
,
library
}
=
useWeb3React
()
const
global
BlockNumber
=
useBlockNumber
()
const
last
BlockNumber
=
useBlockNumber
()
const
dispatch
=
useDispatch
<
AppDispatch
>
()
const
transactions
=
useSelector
<
AppState
>
(
state
=>
state
.
transactions
)
...
...
@@ -18,64 +19,69 @@ export default function Updater() {
// show popup on confirm
const
addPopup
=
useAddPopup
()
const
{
data
:
transactionCount
}
=
useSWR
<
number
|
null
>
([
'
accountNonce
'
,
account
,
lastBlockNumber
],
()
=>
{
if
(
!
account
)
return
null
return
library
.
getTransactionCount
(
account
,
'
latest
'
)
})
useEffect
(()
=>
{
if
(
transactionCount
===
null
)
return
dispatch
(
updateTransactionCount
({
address
:
account
,
transactionCount
,
chainId
}))
},
[
transactionCount
,
account
,
chainId
,
dispatch
])
useEffect
(()
=>
{
if
((
chainId
||
chainId
===
0
)
&&
library
)
{
let
stale
=
false
if
(
typeof
chainId
===
'
number
'
&&
library
)
{
Object
.
keys
(
allTransactions
)
.
filter
(
hash
=>
!
allTransactions
[
hash
].
receipt
)
.
filter
(
hash
=>
!
allTransactions
[
hash
].
receipt
&&
allTransactions
[
hash
].
blockNumberChecked
!==
globalBlockNumber
hash
=>
!
allTransactions
[
hash
].
blockNumberChecked
||
allTransactions
[
hash
].
blockNumberChecked
<
lastBlockNumber
)
.
forEach
(
hash
=>
{
library
.
getTransactionReceipt
(
hash
)
.
then
(
receipt
=>
{
if
(
!
stale
)
{
if
(
!
receipt
)
{
dispatch
(
checkTransaction
({
chainId
,
hash
,
blockNumber
:
globalBlockNumber
}))
}
else
{
dispatch
(
finalizeTransaction
({
chainId
,
if
(
!
receipt
)
{
dispatch
(
checkTransaction
({
chainId
,
hash
,
blockNumber
:
lastBlockNumber
}))
}
else
{
dispatch
(
finalizeTransaction
({
chainId
,
hash
,
receipt
:
{
blockHash
:
receipt
.
blockHash
,
blockNumber
:
receipt
.
blockNumber
,
contractAddress
:
receipt
.
contractAddress
,
from
:
receipt
.
from
,
status
:
receipt
.
status
,
to
:
receipt
.
to
,
transactionHash
:
receipt
.
transactionHash
,
transactionIndex
:
receipt
.
transactionIndex
}
})
)
// add success or failure popup
if
(
receipt
.
status
===
1
)
{
addPopup
({
txn
:
{
hash
,
receipt
:
{
blockHash
:
receipt
.
blockHash
,
blockNumber
:
receipt
.
blockNumber
,
contractAddress
:
receipt
.
contractAddress
,
from
:
receipt
.
from
,
status
:
receipt
.
status
,
to
:
receipt
.
to
,
transactionHash
:
receipt
.
transactionHash
,
transactionIndex
:
receipt
.
transactionIndex
}
})
)
// add success or failure popup
if
(
receipt
.
status
===
1
)
{
addPopup
({
txn
:
{
hash
,
success
:
true
,
summary
:
allTransactions
[
hash
]?.
summary
}
})
}
else
{
addPopup
({
txn
:
{
hash
,
success
:
false
,
summary
:
allTransactions
[
hash
]?.
summary
}
})
}
success
:
true
,
summary
:
allTransactions
[
hash
]?.
summary
}
})
}
else
{
addPopup
({
txn
:
{
hash
,
success
:
false
,
summary
:
allTransactions
[
hash
]?.
summary
}
})
}
}
})
.
catch
(
()
=>
{
dispatch
(
checkTransaction
({
chainId
,
hash
,
blockNumber
:
globalBlockNumber
})
)
.
catch
(
error
=>
{
console
.
error
(
`failed to check transaction hash:
${
hash
}
`
,
error
)
})
})
return
()
=>
{
stale
=
true
}
}
},
[
chainId
,
library
,
allTransactions
,
global
BlockNumber
,
dispatch
,
addPopup
])
},
[
chainId
,
library
,
allTransactions
,
last
BlockNumber
,
dispatch
,
addPopup
])
return
null
}
src/state/wallet/hooks.ts
View file @
5405648a
import
{
getAddress
}
from
'
@ethersproject/address
'
import
{
JSBI
,
Token
,
TokenAmount
,
WETH
}
from
'
@uniswap/sdk
'
import
{
useEffect
,
useMemo
}
from
'
react
'
import
{
useDispatch
,
useSelector
}
from
'
react-redux
'
import
{
useAllTokens
}
from
'
../../contexts/Tokens
'
import
{
useWeb3React
}
from
'
../../hooks
'
import
{
use
Previous
,
use
Web3React
}
from
'
../../hooks
'
import
{
isAddress
}
from
'
../../utils
'
import
{
AppDispatch
,
AppState
}
from
'
../index
'
import
{
...
...
@@ -21,16 +22,29 @@ export function useETHBalances(uncheckedAddresses?: (string | undefined)[]): { [
const
dispatch
=
useDispatch
<
AppDispatch
>
()
const
{
chainId
}
=
useWeb3React
()
const
addresses
:
string
[]
=
useMemo
(()
=>
(
uncheckedAddresses
?
uncheckedAddresses
.
filter
(
isAddress
)
:
[]),
[
uncheckedAddresses
])
const
addresses
:
string
[]
=
useMemo
(
()
=>
uncheckedAddresses
?
uncheckedAddresses
.
filter
(
isAddress
)
.
map
(
getAddress
)
.
sort
()
:
[],
[
uncheckedAddresses
]
)
const
previousAddresses
=
usePrevious
(
addresses
)
const
unchanged
=
JSON
.
stringify
(
previousAddresses
)
===
JSON
.
stringify
(
addresses
)
// add the listeners on mount, remove them on dismount
useEffect
(()
=>
{
if
(
unchanged
)
return
if
(
addresses
.
length
===
0
)
return
dispatch
(
startListeningForBalance
({
addresses
}))
return
()
=>
dispatch
(
stopListeningForBalance
({
addresses
}))
},
[
addresses
,
dispatch
])
if
(
addresses
.
length
>
0
)
{
return
()
=>
dispatch
(
stopListeningForBalance
({
addresses
}))
}
},
[
addresses
,
unchanged
,
dispatch
])
const
rawBalanceMap
=
useSelector
<
AppState
>
(({
wallet
:
{
balances
}
})
=>
balances
)
...
...
@@ -57,15 +71,22 @@ export function useTokenBalances(
const
{
chainId
}
=
useWeb3React
()
const
validTokens
:
Token
[]
=
useMemo
(()
=>
tokens
?.
filter
(
t
=>
isAddress
(
t
?.
address
))
??
[],
[
tokens
])
const
tokenAddresses
:
string
[]
=
useMemo
(()
=>
validTokens
.
map
(
t
=>
t
.
address
).
sort
(),
[
validTokens
])
const
previousTokenAddresses
=
usePrevious
(
tokenAddresses
)
const
unchanged
=
JSON
.
stringify
(
tokenAddresses
)
===
JSON
.
stringify
(
previousTokenAddresses
)
// keep the listeners up to date
useEffect
(()
=>
{
if
(
address
&&
validTokens
.
length
>
0
)
{
const
combos
:
TokenBalanceListenerKey
[]
=
validTokens
.
map
(
token
=>
({
address
,
tokenAddress
:
token
.
address
}))
dispatch
(
startListeningForTokenBalances
(
combos
))
if
(
unchanged
)
return
if
(
!
address
)
return
if
(
tokenAddresses
.
length
===
0
)
return
const
combos
:
TokenBalanceListenerKey
[]
=
tokenAddresses
.
map
(
tokenAddress
=>
({
address
,
tokenAddress
}))
dispatch
(
startListeningForTokenBalances
(
combos
))
if
(
combos
.
length
>
0
)
{
return
()
=>
dispatch
(
stopListeningForTokenBalances
(
combos
))
}
},
[
address
,
validTokens
,
dispatch
])
},
[
address
,
tokenAddresses
,
unchanged
,
dispatch
])
const
rawBalanceMap
=
useSelector
<
AppState
>
(({
wallet
:
{
balances
}
})
=>
balances
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment