Skip to content

Commit

Permalink
Merge pull request #50 from archerdao/dev
Browse files Browse the repository at this point in the history
UX improvements
  • Loading branch information
chpiatt authored May 25, 2021
2 parents e6e63b5 + b5277aa commit 13d1e9d
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 6 deletions.
21 changes: 18 additions & 3 deletions packages/frontend/src/components/AccountDetails/Transaction.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ import { finalizeTransaction } from '../../state/transactions/actions'
import { useDispatch } from 'react-redux'
import { AppDispatch } from '../../state'
import { TransactionDetails } from 'state/transactions/reducer'
import {
useSwapState
} from 'state/swap/hooks'
import { SwapState, LOCAL_STORAGE_KEY_SWAP_STATE } from 'state/swap/reducer'
import useLocalStorage from 'hooks/useLocalStorage'
import { resetSwapState } from 'state/swap/actions'

const TransactionWrapper = styled.div``

Expand Down Expand Up @@ -96,12 +102,20 @@ export default function Transaction({ hash }: { hash: string }) {
const expired = secondsUntilDeadline === -1
const pending = !mined && !cancelled && !expired
const success = !pending && tx && tx.receipt?.status === 1
const swapState = useSwapState();
const [lastTxSwapState] = useLocalStorage<SwapState>(LOCAL_STORAGE_KEY_SWAP_STATE, swapState)

const cancelPending = useCallback(() => {
if (!chainId) return
if (!chainId) {
dispatch(resetSwapState(lastTxSwapState))
return
}

const relayURI = ARCHER_RELAY_URI[chainId]
if (!relayURI) return
if (!relayURI) {
dispatch(resetSwapState(lastTxSwapState))
return
}

const body = JSON.stringify({
method: 'archer_cancelTx',
Expand Down Expand Up @@ -132,9 +146,10 @@ export default function Transaction({ hash }: { hash: string }) {
}
})
)
dispatch(resetSwapState(lastTxSwapState))
})
.catch(err => console.error(err))
}, [dispatch, chainId, relay, hash])
}, [dispatch, chainId, relay, hash, lastTxSwapState])

if (!chainId) return null

Expand Down
81 changes: 81 additions & 0 deletions packages/frontend/src/hooks/useLocalStorage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { useEffect, useState } from 'react'

function useLocalStorage<T>(
key: string,
initialValue: T,
): [T, (value: T) => void] {
// Get from local storage then
// parse stored json or return initialValue
const readValue = () => {
// Prevent build error "window is undefined" but keep keep working
if (typeof window === 'undefined') {
return initialValue
}

try {
const item = window.localStorage.getItem(key)
return item ? JSON.parse(item) : initialValue
} catch (error) {
console.warn(`Error reading localStorage key “${key}”:`, error)
return initialValue
}
}

// State to store our value
// Pass initial state function to useState so logic is only executed once
const [storedValue, setStoredValue] = useState<T>(readValue)

// Return a wrapped version of useState's setter function that ...
// ... persists the new value to localStorage.
const setValue = (value: T) => {
// Prevent build error "window is undefined" but keeps working
if (typeof window == 'undefined') {
console.warn(
`Tried setting localStorage key “${key}” even though environment is not a client`,
)
}

try {
// Allow value to be a function so we have the same API as useState
const newValue = value instanceof Function ? value(storedValue) : value

// Save to local storage
window.localStorage.setItem(key, JSON.stringify(newValue))

// Save state
setStoredValue(newValue)

// We dispatch a custom event so every useLocalStorage hook are notified
window.dispatchEvent(new Event('local-storage'))
} catch (error) {
console.warn(`Error setting localStorage key “${key}”:`, error)
}
}

useEffect(() => {
setStoredValue(readValue())
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])

useEffect(() => {
const handleStorageChange = () => {
setStoredValue(readValue())
}

// this only works for other documents, not the current one
window.addEventListener('storage', handleStorageChange)

// this is a custom event, triggered in writeValueToLocalStorage
window.addEventListener('local-storage', handleStorageChange)

return () => {
window.removeEventListener('storage', handleStorageChange)
window.removeEventListener('local-storage', handleStorageChange)
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])

return [storedValue, setValue]
}

export default useLocalStorage
17 changes: 15 additions & 2 deletions packages/frontend/src/pages/Swap/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import SwapHeader from '../../components/swap/SwapHeader'
import MinerTip from './MinerTip';

import { ARCHER_RELAY_URI, ARCHER_ROUTER_ADDRESS } from '../../constants'
import { SwapState, LOCAL_STORAGE_KEY_SWAP_STATE } from 'state/swap/reducer'
import { getTradeVersion } from '../../data/V1'
import { useActiveWeb3React } from '../../hooks'
import { useCurrency, useAllTokens } from '../../hooks/Tokens'
Expand All @@ -31,6 +32,7 @@ import useENSAddress from '../../hooks/useENSAddress'
import { useSwapCallback } from '../../hooks/useSwapCallback'
import useWrapCallback, { WrapType } from '../../hooks/useWrapCallback'
import { useToggleSettingsMenu, useWalletModalToggle } from '../../state/application/hooks'
import useLocalStorage from 'hooks/useLocalStorage'
import { Field } from '../../state/swap/actions'
import {
useDefaultsFromURLSearch,
Expand Down Expand Up @@ -92,7 +94,9 @@ export default function Swap({ history }: RouteComponentProps) {
const [ethTip] = useUserETHTip()

// swap state
const { independentField, typedValue, recipient } = useSwapState()
const swapState = useSwapState()
const { independentField, typedValue, recipient } = swapState
const [,setLastTxSwapState] = useLocalStorage<SwapState>(LOCAL_STORAGE_KEY_SWAP_STATE, swapState)
const {
v2Trade,
currencyBalances,
Expand Down Expand Up @@ -211,6 +215,7 @@ export default function Swap({ history }: RouteComponentProps) {
swapCallback()
.then(hash => {
setSwapState({ attemptingTxn: false, tradeToConfirm, showConfirm, swapErrorMessage: undefined, txHash: hash })
setLastTxSwapState(swapState);

ReactGA.event({
category: 'Swap',
Expand Down Expand Up @@ -246,11 +251,13 @@ export default function Swap({ history }: RouteComponentProps) {
swapCallback,
tradeToConfirm,
showConfirm,
setLastTxSwapState,
recipient,
recipientAddress,
account,
trade,
singleHopOnly
singleHopOnly,
swapState
])

// errors
Expand Down Expand Up @@ -296,6 +303,12 @@ export default function Swap({ history }: RouteComponentProps) {
onCurrencySelection
])

useEffect(() => {
if(parsedAmounts[Field.INPUT] && maxAmountInput && parsedAmounts[Field.INPUT]?.greaterThan(maxAmountInput)) {
handleMaxInput();
}
}, [handleMaxInput, parsedAmounts, maxAmountInput]);

const swapIsUnsupported = useIsTransactionUnsupported(currencies?.INPUT, currencies?.OUTPUT)

return (
Expand Down
2 changes: 2 additions & 0 deletions packages/frontend/src/state/swap/actions.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { createAction } from '@reduxjs/toolkit'
import { SwapState } from './reducer'

export enum Field {
INPUT = 'INPUT',
Expand All @@ -15,4 +16,5 @@ export const replaceSwapState = createAction<{
outputCurrencyId?: string
recipient: string | null
}>('swap/replaceSwapState')
export const resetSwapState = createAction<SwapState>('swap/resetSwapState')
export const setRecipient = createAction<{ recipient: string | null }>('swap/setRecipient')
5 changes: 4 additions & 1 deletion packages/frontend/src/state/swap/reducer.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { createReducer } from '@reduxjs/toolkit'
import { Field, replaceSwapState, selectCurrency, setRecipient, switchCurrencies, typeInput } from './actions'
import { Field, replaceSwapState, selectCurrency, setRecipient, switchCurrencies, typeInput, resetSwapState } from './actions'

export interface SwapState {
readonly independentField: Field
Expand All @@ -26,6 +26,8 @@ const initialState: SwapState = {
recipient: null
}

export const LOCAL_STORAGE_KEY_SWAP_STATE = 'archerswap_swap_state'

export default createReducer<SwapState>(initialState, builder =>
builder
.addCase(
Expand Down Expand Up @@ -80,4 +82,5 @@ export default createReducer<SwapState>(initialState, builder =>
.addCase(setRecipient, (state, { payload: { recipient } }) => {
state.recipient = recipient
})
.addCase(resetSwapState, (state, { payload: newState }) => newState)
)

0 comments on commit 13d1e9d

Please sign in to comment.