diff --git a/src/components/Send/SendForm.tsx b/src/components/Send/SendForm.tsx index 51c93738..9f387fbe 100644 --- a/src/components/Send/SendForm.tsx +++ b/src/components/Send/SendForm.tsx @@ -228,8 +228,6 @@ interface InnerSendFormProps { feeConfigValues?: FeeValues reloadFeeConfigValues: () => void disabled?: boolean - isDisplayReloadInShowUtxos: boolean - setIsDisplayReloadInShowUtxos: (arg: boolean) => void } const InnerSendForm = ({ @@ -243,8 +241,6 @@ const InnerSendForm = ({ feeConfigValues, reloadFeeConfigValues, disabled = false, - isDisplayReloadInShowUtxos, - setIsDisplayReloadInShowUtxos, }: InnerSendFormProps) => { const { t } = useTranslation() const serviceInfo = useServiceInfo() @@ -283,8 +279,6 @@ const InnerSendForm = ({ isLoading={isLoading} disabled={disabled} variant={showCoinjoinPreconditionViolationAlert ? 'warning' : 'default'} - isDisplayReloadInShowUtxos={isDisplayReloadInShowUtxos} - setIsDisplayReloadInShowUtxos={setIsDisplayReloadInShowUtxos} /> {showCoinjoinPreconditionViolationAlert && (
@@ -386,8 +380,6 @@ type SendFormProps = Omit & { formRef?: React.Ref> blurred?: boolean wallet: CurrentWallet - isDisplayReloadInShowUtxos: boolean - setIsDisplayReloadInShowUtxos: (arg: boolean) => void } export const SendForm = ({ diff --git a/src/components/Send/ShowUtxos.tsx b/src/components/Send/ShowUtxos.tsx old mode 100644 new mode 100755 index 4d480a15..03eacd75 --- a/src/components/Send/ShowUtxos.tsx +++ b/src/components/Send/ShowUtxos.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect, useCallback, memo, useRef, useMemo } from 'react' +import { useState, useEffect, useCallback, memo, useMemo } from 'react' import * as rb from 'react-bootstrap' import { useTranslation } from 'react-i18next' import type { TFunction } from 'i18next' @@ -6,14 +6,7 @@ import classNames from 'classnames' import { Table, Body, Row, Cell } from '@table-library/react-table-library/table' import { useTheme } from '@table-library/react-table-library/theme' import * as TableTypes from '@table-library/react-table-library/types/table' -import * as Api from '../../libs/JmWalletApi' -import { - WalletInfo, - CurrentWallet, - useReloadCurrentWalletInfo, - Utxo, - useCurrentWalletInfo, -} from '../../context/WalletContext' +import { WalletInfo, Utxo, useCurrentWalletInfo } from '../../context/WalletContext' import { useSettings, Settings } from '../../context/SettingsContext' import Alert from '../Alert' import Balance from '../Balance' @@ -22,17 +15,18 @@ import Sprite from '../Sprite' import { utxoTags } from '../jar_details/UtxoList' import mainStyles from '../MainWalletView.module.css' import styles from './ShowUtxos.module.css' - -type UtxoList = Array +import { UtxoList } from './SourceJarSelector' interface ShowUtxosProps { - walletInfo: WalletInfo - wallet: CurrentWallet isOpen: boolean onCancel: () => void - jarIndex: String - isDisplayReloadInShowUtxos: boolean - setIsDisplayReloadInShowUtxos: (arg: boolean) => void + onConfirm: () => void + alert: SimpleAlert | undefined + isLoading: boolean + frozenUtxos: UtxoList + unFrozenUtxos: UtxoList + setFrozenUtxos: (arg: UtxoList) => void + setUnFrozenUtxos: (arg: UtxoList) => void } interface UtxoRowProps { @@ -286,135 +280,49 @@ const Divider = ({ isState, setIsState, className }: DividerProps) => { } const ShowUtxos = ({ - walletInfo, - wallet, isOpen, onCancel, - jarIndex, - isDisplayReloadInShowUtxos, - setIsDisplayReloadInShowUtxos, + onConfirm, + alert, + isLoading, + frozenUtxos, + unFrozenUtxos, + setFrozenUtxos, + setUnFrozenUtxos, }: ShowUtxosProps) => { - const [alert, setAlert] = useState(undefined) - const [showFrozenUtxos, setShowFrozenUtxos] = useState(false) - const [unFrozenUtxos, setUnFrozenUtxos] = useState([]) - const [frozenUtxos, setFrozenUtxos] = useState([]) - const [isLoading, setIsLoading] = useState(true) const { t } = useTranslation() - const reloadCurrentWalletInfo = useReloadCurrentWalletInfo() const settings = useSettings() + const [showFrozenUtxos, setShowFrozenUtxos] = useState(false) - const isHandleReloadExecuted = useRef(false) - - // Load data from wallet info - const loadData = useCallback( - (walletInfo: WalletInfo) => { - const data = Object.entries(walletInfo.utxosByJar).find(([key]) => key === jarIndex) - const utxos: any = data ? data[1] : [] - - const frozenUtxoList = utxos - .filter((utxo: any) => utxo.frozen) - .map((utxo: any) => ({ ...utxo, id: utxo.utxo, checked: false })) - const unFrozenUtxosList = utxos - .filter((utxo: any) => !utxo.frozen) - .map((utxo: any) => ({ ...utxo, id: utxo.utxo, checked: true })) - - setFrozenUtxos(frozenUtxoList) - setUnFrozenUtxos(unFrozenUtxosList) - - if (unFrozenUtxosList.length === 0) { - setShowFrozenUtxos(true) - setAlert({ variant: 'warning', message: t('show_utxos.alert_for_unfreeze_utxos'), dismissible: true }) + // Handler to toggle UTXO selection + const handleUtxoCheckedState = useCallback( + (utxoIndex: number, isFrozen: boolean) => { + if (!isFrozen) { + const utxos = unFrozenUtxos.map((utxo: Utxo, i: number) => + i === utxoIndex ? { ...utxo, checked: !utxo.checked } : utxo, + ) + setUnFrozenUtxos(utxos) } else { - setAlert(undefined) + const utxos = frozenUtxos.map((utxo: Utxo, i: number) => + i === utxoIndex ? { ...utxo, checked: !utxo.checked } : utxo, + ) + setFrozenUtxos(utxos) } }, - [jarIndex, t], + [frozenUtxos, unFrozenUtxos, setUnFrozenUtxos, setFrozenUtxos], ) - // Reload wallet info - const handleReload = useCallback(async () => { - const abortCtrl = new AbortController() - try { - setIsLoading(true) - await reloadCurrentWalletInfo.reloadUtxos({ signal: abortCtrl.signal }) - if (isDisplayReloadInShowUtxos) { - await reloadCurrentWalletInfo.reloadDisplay({ signal: abortCtrl.signal }) - setIsDisplayReloadInShowUtxos(false) - } - loadData(walletInfo) - setIsLoading(false) - } catch (err: any) { - if (!abortCtrl.signal.aborted) { - setAlert({ variant: 'danger', message: err.message, dismissible: true }) - } - } - }, [isDisplayReloadInShowUtxos, setIsDisplayReloadInShowUtxos, reloadCurrentWalletInfo, loadData, walletInfo]) - - //Effect to Reload walletInfo only once - useEffect(() => { - if (!isHandleReloadExecuted.current) { - handleReload() - isHandleReloadExecuted.current = true - } - }, [handleReload]) - - //Effect to set Alert according to the walletInfo + //Effect to hide the Divider line when there is no unFrozen-UTXOs present useEffect(() => { - const frozenUtxosToUpdate = frozenUtxos.filter((utxo: Utxo) => utxo.checked && !utxo.locktime) - const timeLockedUtxo = frozenUtxos.find((utxo: Utxo) => utxo.checked && utxo.locktime) - const allUnFrozenUnchecked = unFrozenUtxos.every((utxo: Utxo) => !utxo.checked) - - if (timeLockedUtxo) { - setAlert({ variant: 'danger', message: `${t('show_utxos.alert_for_time_locked')} ${timeLockedUtxo.locktime}` }) - } else if (allUnFrozenUnchecked && frozenUtxosToUpdate.length === 0) { - setAlert({ variant: 'warning', message: t('show_utxos.alert_for_unfreeze_utxos'), dismissible: true }) - } else if (unFrozenUtxos.length !== 0 || frozenUtxosToUpdate.length !== 0) { - setAlert(undefined) - } - }, [frozenUtxos, unFrozenUtxos, t]) - - // Handler to toggle UTXO selection - const handleToggle = useCallback((utxoIndex: number, isFrozen: boolean) => { - if (!isFrozen) { - setUnFrozenUtxos((prevUtxos) => - prevUtxos.map((utxo, i) => (i === utxoIndex ? { ...utxo, checked: !utxo.checked } : utxo)), - ) - } else { - setFrozenUtxos((prevUtxos) => - prevUtxos.map((utxo, i) => (i === utxoIndex ? { ...utxo, checked: !utxo.checked } : utxo)), - ) + if (unFrozenUtxos.length === 0 && frozenUtxos.length > 0) { + setShowFrozenUtxos(true) } - }, []) - - // Handler for the "confirm" button click - const handleConfirm = async () => { - const abortCtrl = new AbortController() - - const frozenUtxosToUpdate = frozenUtxos - .filter((utxo) => utxo.checked && !utxo.locktime) - .map((utxo) => ({ utxo: utxo.utxo, freeze: false })) - const unFrozenUtxosToUpdate = unFrozenUtxos - .filter((utxo) => !utxo.checked) - .map((utxo) => ({ utxo: utxo.utxo, freeze: true })) - - try { - await Promise.all([ - ...frozenUtxosToUpdate.map((utxo) => Api.postFreeze({ ...wallet, signal: abortCtrl.signal }, utxo)), - ...unFrozenUtxosToUpdate.map((utxo) => Api.postFreeze({ ...wallet, signal: abortCtrl.signal }, utxo)), - ]) - await handleReload() - onCancel() - } catch (err: any) { - if (!abortCtrl.signal.aborted) { - setAlert({ variant: 'danger', message: err.message, dismissible: true }) - } - } - } + }, [unFrozenUtxos.length, frozenUtxos.length]) return ( {alert && ( - setAlert(undefined)} - /> + )} void } interface ShowUtxosProps { @@ -26,6 +25,8 @@ interface ShowUtxosProps { isOpen: boolean } +export type UtxoList = Utxo[] + export const SourceJarSelector = ({ name, label, @@ -34,17 +35,20 @@ export const SourceJarSelector = ({ variant, isLoading, disabled = false, - isDisplayReloadInShowUtxos, - setIsDisplayReloadInShowUtxos, }: SourceJarSelectorProps) => { const { t } = useTranslation() - const [field] = useField(name) const form = useFormikContext() + const reloadCurrentWalletInfo = useReloadCurrentWalletInfo() + const [showUtxos, setShowUtxos] = useState({ jarIndex: '', isOpen: false, }) + const [alert, setAlert] = useState(undefined) + const [isUtxosLoading, setIsUtxosLoading] = useState(false) + const [unFrozenUtxos, setUnFrozenUtxos] = useState([]) + const [frozenUtxos, setFrozenUtxos] = useState([]) const jarBalances = useMemo(() => { if (!walletInfo) return [] @@ -53,6 +57,76 @@ export const SourceJarSelector = ({ ) }, [walletInfo]) + useEffect(() => { + if (showUtxos.jarIndex && walletInfo?.utxosByJar) { + const data = Object.entries(walletInfo.utxosByJar).find(([key]) => key === showUtxos.jarIndex) + const utxos: any = data ? data[1] : [] + + const frozenUtxoList = utxos + .filter((utxo: any) => utxo.frozen) + .map((utxo: any) => ({ ...utxo, id: utxo.utxo, checked: false })) + const unFrozenUtxosList = utxos + .filter((utxo: any) => !utxo.frozen) + .map((utxo: any) => ({ ...utxo, id: utxo.utxo, checked: true })) + + setFrozenUtxos(frozenUtxoList) + setUnFrozenUtxos(unFrozenUtxosList) + } + }, [walletInfo, showUtxos.jarIndex, t]) + + useEffect(() => { + if (frozenUtxos.length === 0 && unFrozenUtxos.length === 0) { + return + } + const frozenUtxosToUpdate = frozenUtxos.filter((utxo: Utxo) => utxo.checked && !utxo.locktime) + const timeLockedUtxo = frozenUtxos.find((utxo: Utxo) => utxo.checked && utxo.locktime) + const allUnFrozenUnchecked = unFrozenUtxos.every((utxo: Utxo) => !utxo.checked) + + if (frozenUtxos.length > 0 && timeLockedUtxo) { + setAlert({ variant: 'danger', message: `${t('show_utxos.alert_for_time_locked')} ${timeLockedUtxo.locktime}` }) + } else if ( + (frozenUtxos.length > 0 || unFrozenUtxos.length > 0) && + allUnFrozenUnchecked && + frozenUtxosToUpdate.length === 0 + ) { + setAlert({ variant: 'warning', message: t('show_utxos.alert_for_unfreeze_utxos'), dismissible: true }) + } else { + setAlert(undefined) + } + }, [frozenUtxos, unFrozenUtxos, t, setAlert]) + + const handleUtxosFrozenState = useCallback(async () => { + const abortCtrl = new AbortController() + const frozenUtxosToUpdate = frozenUtxos + .filter((utxo) => utxo.checked && !utxo.locktime) + .map((utxo) => ({ utxo: utxo.utxo, freeze: false })) + const unFrozenUtxosToUpdate = unFrozenUtxos + .filter((utxo) => !utxo.checked) + .map((utxo) => ({ utxo: utxo.utxo, freeze: true })) + + try { + const res = await Promise.all([ + ...frozenUtxosToUpdate.map((utxo) => Api.postFreeze({ ...wallet, signal: abortCtrl.signal }, utxo)), + ...unFrozenUtxosToUpdate.map((utxo) => Api.postFreeze({ ...wallet, signal: abortCtrl.signal }, utxo)), + ]) + + if (res.length !== 0) { + setIsUtxosLoading(true) + await reloadCurrentWalletInfo.reloadUtxos({ signal: abortCtrl.signal }) + setIsUtxosLoading(false) + } + + setShowUtxos({ + jarIndex: '', + isOpen: false, + }) + } catch (err: any) { + if (!abortCtrl.signal.aborted) { + setAlert({ variant: 'danger', message: err.message, dismissible: true }) + } + } + }, [frozenUtxos, unFrozenUtxos, wallet, reloadCurrentWalletInfo]) + return ( <> @@ -65,18 +139,20 @@ export const SourceJarSelector = ({
{showUtxos.isOpen && ( { setShowUtxos({ jarIndex: '', isOpen: false, }) }} - jarIndex={showUtxos.jarIndex} - isDisplayReloadInShowUtxos={isDisplayReloadInShowUtxos} - setIsDisplayReloadInShowUtxos={setIsDisplayReloadInShowUtxos} + alert={alert} + isLoading={isUtxosLoading} + frozenUtxos={frozenUtxos} + unFrozenUtxos={unFrozenUtxos} + setFrozenUtxos={setFrozenUtxos} + setUnFrozenUtxos={setUnFrozenUtxos} /> )} {jarBalances.map((it) => { diff --git a/src/components/Send/index.tsx b/src/components/Send/index.tsx index 52850be6..fefbdc5b 100644 --- a/src/components/Send/index.tsx +++ b/src/components/Send/index.tsx @@ -91,8 +91,6 @@ export default function Send({ wallet }: SendProps) { const reloadServiceInfo = useReloadServiceInfo() const loadConfigValue = useLoadConfigValue() - const [isDisplayReloadInShowUtxos, setIsDisplayReloadInShowUtxos] = useState(true) - const isCoinjoinInProgress = useMemo(() => serviceInfo?.coinjoinInProgress === true, [serviceInfo]) const isMakerRunning = useMemo(() => serviceInfo?.makerRunning === true, [serviceInfo]) const isRescanningInProgress = useMemo(() => serviceInfo?.rescanning === true, [serviceInfo]) @@ -204,7 +202,7 @@ export default function Send({ wallet }: SendProps) { }) const loadingWalletInfoAndUtxos = reloadCurrentWalletInfo - .reloadUtxos({ signal: abortCtrl.signal }) + .reloadDisplay({ signal: abortCtrl.signal }) .catch((err) => { if (abortCtrl.signal.aborted) return const message = t('global.errors.error_loading_wallet_failed', { @@ -268,7 +266,6 @@ export default function Send({ wallet }: SendProps) { txid, }), }) - setIsDisplayReloadInShowUtxos(true) setWaitForUtxosToBeSpent(inputs.map((it: any) => it.outpoint)) success = true } else { @@ -493,8 +490,6 @@ export default function Send({ wallet }: SendProps) { loadNewWalletAddress={loadNewWalletAddress} feeConfigValues={feeConfigValues} reloadFeeConfigValues={reloadFeeConfigValues} - isDisplayReloadInShowUtxos={isDisplayReloadInShowUtxos} - setIsDisplayReloadInShowUtxos={setIsDisplayReloadInShowUtxos} /> {showConfirmAbortModal && (