Skip to content

Commit

Permalink
refactor: speed up initial page load (#566)
Browse files Browse the repository at this point in the history
* refactor: prepare removing display request

* refactor: Navbar from jsx to tsx

* refactor: SettingsContext from jsx to tsx

* refactor: use calculated balance in sats instead of totalBalance as string

* refactor: remove totalBalance and availableBalance from BalanceSummary

* refactor: simplify navbar states

* refactor(WalletContext): ability to independently load data

* refactor(JarSelectorModal): allow async confirm operations

* refactor(Send): always fetch new jar destination address

* refactor(WalletContext): return response data from reload functions

* refactor(Send): speed up loading Send page

* refactor(CreateFidelityBond): speed up waiting for fb utxo

* refactor(MainWalletView): jsx to tsx

* refactor(MainWalletView): speed up loading MainWalletView page

* refactor(Jam): speed up loading Jam page

* fix: prevent artifact on wallet with zero balance

* refactor(Earn): speed up loading Earn page
  • Loading branch information
theborakompanioni committed Dec 9, 2022
1 parent 495dc2b commit e2bad18
Show file tree
Hide file tree
Showing 10 changed files with 385 additions and 256 deletions.
23 changes: 12 additions & 11 deletions src/components/Earn.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect, useState } from 'react'
import { useEffect, useMemo, useState } from 'react'
import { Formik } from 'formik'
import * as rb from 'react-bootstrap'
import { useTranslation } from 'react-i18next'
Expand Down Expand Up @@ -183,7 +183,9 @@ export default function Earn({ wallet }) {
const [isWaitingMakerStop, setIsWaitingMakerStop] = useState(false)
const [isShowReport, setIsShowReport] = useState(false)
const [isShowOrderbook, setIsShowOrderbook] = useState(false)
const [fidelityBonds, setFidelityBonds] = useState([])
const fidelityBonds = useMemo(() => {
return currentWalletInfo?.fidelityBondSummary.fbOutputs || []
}, [currentWalletInfo])

const startMakerService = (ordertype, minsize, cjfee_a, cjfee_r) => {
setIsSending(true)
Expand Down Expand Up @@ -238,11 +240,7 @@ export default function Earn({ wallet }) {
setIsLoading(true)

const reloadingServiceInfo = reloadServiceInfo({ signal: abortCtrl.signal })
const reloadingCurrentWalletInfo = reloadCurrentWalletInfo({ signal: abortCtrl.signal }).then((info) => {
if (!abortCtrl.signal.aborted) {
setFidelityBonds(info.fidelityBondSummary.fbOutputs)
}
})
const reloadingCurrentWalletInfo = reloadCurrentWalletInfo.reloadUtxos({ signal: abortCtrl.signal })

Promise.all([reloadingServiceInfo, reloadingCurrentWalletInfo])
.catch((err) => {
Expand All @@ -251,7 +249,7 @@ export default function Earn({ wallet }) {
.finally(() => !abortCtrl.signal.aborted && setIsLoading(false))

return () => abortCtrl.abort()
}, [wallet, isSending, reloadServiceInfo, reloadCurrentWalletInfo])
}, [isSending, reloadServiceInfo, reloadCurrentWalletInfo])

useEffect(() => {
if (isSending) return
Expand Down Expand Up @@ -283,14 +281,17 @@ export default function Earn({ wallet }) {

new Promise((resolve) => {
setTimeout(() => {
resolve(reloadCurrentWalletInfo({ signal: abortCtrl.signal }))
resolve(reloadCurrentWalletInfo.reloadUtxos({ signal: abortCtrl.signal }))
}, delay)
})
.then((info) => setFidelityBonds(info.fidelityBondSummary.fbOutputs))
.catch((err) => {
if (abortCtrl.signal.aborted) return
setAlert({ variant: 'danger', message: err.message })
})
.finally(() => !abortCtrl.signal.aborted && setIsLoading(false))
.finally(() => {
if (abortCtrl.signal.aborted) return
setIsLoading(false)
})
}

const feeRelMin = 0.0
Expand Down
71 changes: 26 additions & 45 deletions src/components/Jam.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useState, useEffect, useCallback, useMemo } from 'react'
import { useState, useEffect, useMemo } from 'react'
import * as rb from 'react-bootstrap'
import { useTranslation } from 'react-i18next'
import { Formik, useFormikContext } from 'formik'
Expand All @@ -21,6 +21,23 @@ import styles from './Jam.module.css'
const DEST_ADDRESS_COUNT_PROD = 3
const DEST_ADDRESS_COUNT_TEST = 1

const getNewAddressesForTesting = (walletInfo, count, mixdepth = 0) => {
if (!walletInfo) {
throw new Error('Wallet info is not available.')
}
const externalBranch = walletInfo.data.display.walletinfo.accounts[mixdepth].branches.find((branch) => {
return branch.branch.split('\t')[0] === 'external addresses'
})

const newEntries = externalBranch.entries.filter((entry) => entry.status === 'new').slice(0, count)

if (newEntries.length !== count) {
throw new Error(`Cannot find enough fresh addresses in mixdepth ${mixdepth}`)
}

return newEntries.map((it) => it.address)
}

const addressValueKeys = (addressCount) =>
Array(addressCount)
.fill('')
Expand Down Expand Up @@ -66,27 +83,6 @@ export default function Jam({ wallet }) {
[walletInfo]
)

// Returns one fresh address for each requested mixdepth.
const getNewAddresses = useCallback(
(count, mixdepth = 0) => {
if (!walletInfo) {
throw new Error('Wallet info is not available.')
}
const externalBranch = walletInfo.data.display.walletinfo.accounts[mixdepth].branches.find((branch) => {
return branch.branch.split('\t')[0] === 'external addresses'
})

const newEntries = externalBranch.entries.filter((entry) => entry.status === 'new').slice(0, count)

if (newEntries.length !== count) {
throw new Error(`Cannot find enough fresh addresses in mixdepth ${mixdepth}`)
}

return newEntries.map((it) => it.address)
},
[walletInfo]
)

const [useInsecureTestingSettings, setUseInsecureTestingSettings] = useState(false)
const addressCount = useMemo(
() => (useInsecureTestingSettings ? DEST_ADDRESS_COUNT_TEST : DEST_ADDRESS_COUNT_PROD),
Expand All @@ -98,15 +94,15 @@ export default function Jam({ wallet }) {
if (useInsecureTestingSettings) {
try {
// prefill with addresses marked as "new"
destinationAddresses = getNewAddresses(addressCount)
destinationAddresses = getNewAddressesForTesting(walletInfo, addressCount)
} catch (e) {
// on error initialize with empty addresses - form validation will do the rest
destinationAddresses = Array(addressCount).fill('')
}
}

return destinationAddresses.reduce((obj, addr, index) => ({ ...obj, [`dest${index + 1}`]: addr }), {})
}, [addressCount, useInsecureTestingSettings, getNewAddresses])
}, [addressCount, useInsecureTestingSettings, walletInfo])

useEffect(() => {
setAlert(null)
Expand All @@ -119,7 +115,7 @@ export default function Jam({ wallet }) {
setAlert({ variant: 'danger', message })
})

const loadingWalletInfo = reloadCurrentWalletInfo({ signal: abortCtrl.signal }).catch((err) => {
const loadingWalletInfo = reloadCurrentWalletInfo.reloadUtxos({ signal: abortCtrl.signal }).catch((err) => {
if (abortCtrl.signal.aborted) return
const message = err.message || t('send.error_loading_wallet_failed')
setAlert({ variant: 'danger', message })
Expand All @@ -133,7 +129,7 @@ export default function Jam({ wallet }) {
return () => {
abortCtrl.abort()
}
}, [reloadServiceInfo, reloadCurrentWalletInfo, t])
}, [collaborativeOperationRunning, reloadServiceInfo, reloadCurrentWalletInfo, t])

useEffect(() => {
if (!serviceInfo) return
Expand All @@ -149,24 +145,6 @@ export default function Jam({ wallet }) {
}
}, [serviceInfo])

useEffect(() => {
// Due to polling, using `collaborativeOperationRunning` instead of
// `schedule` here, as a schedule object might still be present when
// the scheduler is actually not running anymore. Reload wallet data
// only when no collaborative operation is running anymore.
if (collaborativeOperationRunning) return

setIsLoading(true)
const abortCtrl = new AbortController()
reloadCurrentWalletInfo({ signal: abortCtrl.signal }).finally(() => {
if (abortCtrl.signal.aborted) return
setIsLoading(false)
})
return () => {
abortCtrl.abort()
}
}, [collaborativeOperationRunning, reloadCurrentWalletInfo])

const startSchedule = async (values) => {
if (isLoading || collaborativeOperationRunning) {
return
Expand Down Expand Up @@ -365,7 +343,10 @@ export default function Jam({ wallet }) {
setUseInsecureTestingSettings(isToggled)
if (isToggled) {
try {
const newAddresses = getNewAddresses(DEST_ADDRESS_COUNT_TEST)
const newAddresses = getNewAddressesForTesting(
walletInfo,
DEST_ADDRESS_COUNT_TEST
)
newAddresses.forEach((newAddress, index) => {
setFieldValue(`dest${index + 1}`, newAddress, true)
})
Expand Down
23 changes: 16 additions & 7 deletions src/components/JarSelectorModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ interface JarSelectorModalProps {
totalBalance: AmountSats
disabledJar?: JarIndex
onCancel: () => void
onConfirm: (jarIndex: JarIndex) => void
onConfirm: (jarIndex: JarIndex) => Promise<void>
}

export default function JarSelectorModal({
Expand All @@ -28,23 +28,26 @@ export default function JarSelectorModal({
}: JarSelectorModalProps) {
const { t } = useTranslation()

const [selectedJar, setSelectedJar] = useState<JarIndex | null>(null)
const [isConfirming, setIsConfirming] = useState(false)
const [selectedJar, setSelectedJar] = useState<JarIndex>()

const sortedAccountBalances = useMemo(() => {
if (!accountBalances) return []
return Object.values(accountBalances).sort((lhs, rhs) => lhs.accountIndex - rhs.accountIndex)
}, [accountBalances])

const cancel = () => {
setSelectedJar(null)
setSelectedJar(undefined)
onCancel()
}

const confirm = () => {
if (selectedJar === null) return
if (selectedJar === undefined) return

setIsConfirming(true)
onConfirm(selectedJar)
setSelectedJar(null)
.then(() => setSelectedJar(undefined))
.finally(() => setIsConfirming(false))
}

return (
Expand Down Expand Up @@ -88,8 +91,14 @@ export default function JarSelectorModal({
<rb.Button variant="light" onClick={cancel} className="d-flex justify-content-center align-items-center">
{t('modal.confirm_button_reject')}
</rb.Button>
<rb.Button disabled={selectedJar === null} variant="dark" onClick={confirm}>
{t('modal.confirm_button_accept')}
<rb.Button disabled={isConfirming || selectedJar === undefined} variant="dark" onClick={confirm}>
{isConfirming ? (
<>
<rb.Spinner as="span" animation="border" size="sm" role="status" />
</>
) : (
<>{t('modal.confirm_button_accept')}</>
)}
</rb.Button>
</rb.Modal.Footer>
</rb.Modal>
Expand Down
15 changes: 8 additions & 7 deletions src/components/Jars.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,16 @@ const Jars = ({ accountBalances, totalBalance, onClick }: JarsProps) => {
return Object.values(accountBalances).sort((lhs, rhs) => lhs.accountIndex - rhs.accountIndex)
}, [accountBalances])

const jarsDescriptionPopover = (
<rb.Popover>
<rb.Popover.Body>{t('current_wallet.jars_title_popover')}</rb.Popover.Body>
</rb.Popover>
)

return (
<div className="d-flex flex-column align-items-center gap-5">
<rb.OverlayTrigger placement="right" overlay={jarsDescriptionPopover}>
<rb.OverlayTrigger
placement="right"
overlay={
<rb.Popover>
<rb.Popover.Body>{t('current_wallet.jars_title_popover')}</rb.Popover.Body>
</rb.Popover>
}
>
<div className={styles.jarsTitle}>
<div>{t('current_wallet.jars_title')}</div>
<Sprite className={styles.infoIcon} symbol="info" width="18" height="18" />
Expand Down
20 changes: 13 additions & 7 deletions src/components/MainWalletView.module.css
Original file line number Diff line number Diff line change
@@ -1,33 +1,39 @@
.wallet-header-title-placeholder {
.walletHeader {
display: flex;
flex-direction: column;
align-items: center;
}

.walletHeader .titlePlaceholder {
width: 5rem;
margin-bottom: 0.5rem;
}

.wallet-header-subtitle-placeholder {
.walletHeader .subtitlePlaceholder {
width: 12rem;
height: 1.8rem;
margin-bottom: 0.45rem;
}

.jars-placeholder {
.jarsPlaceholder {
width: 100%;
height: 3.5rem;
}

.jars-divider-container {
.jarsDividerContainer {
display: flex;
justify-content: space-between;
align-items: center;
}

.jars-divider-line {
.jarsDividerContainer .dividerLine {
margin: 0;
width: 50%;
flex-grow: 0;
flex-shrink: 1;
}

.jars-divider-button {
.jarsDividerContainer .dividerButton {
display: flex;
justify-content: center;
align-items: center;
Expand All @@ -43,7 +49,7 @@
height: 2rem;
}

.send-receive-button {
.sendReceiveButton {
padding: 0.25rem;
font-weight: 500;
border-color: rgba(222, 222, 222, 1) !important;
Expand Down

0 comments on commit e2bad18

Please sign in to comment.