From 44f8af611d9dbc9dab475aea805fa591accc347f Mon Sep 17 00:00:00 2001 From: bigboydiamonds <57741810+bigboydiamonds@users.noreply.github.com> Date: Wed, 27 Mar 2024 09:28:59 -0700 Subject: [PATCH] feat(synapse-interface): destination address (#2137) * Init * Basic Address Input * Detect if input is valid address * Click on Input to trigger warning * DestinationInputWarning to handle accept/reject warning * Hide placeholder when input is active * Input display when input address valid * Input width * DestinationInputWarning state * Warning accept/reject callback * DestinationInputWarning to take up entire output container * Style Warning * Focus on Input after accepting warning terms * showDestinationWarning state in bridge display to track if user already accepted warning for session * Input border red signalling invalid input when activated * Improve isEmptyString * SwitchButton takes priority over OutputContainer in visual layer * Clear Input button * Style and format clear button for smooth input clearing * Style Close Button * Clean * filterTransactionsByRecipient * Calculate days ago a recipient tx was executed * filterNewestTxByRecipient * Display list of recipient addresses when input is focused * Style Receipient dropdown list * Destination address settable from dropdown list * Close dropdown after selecting new destination address from list * Disable Input if wallet unconnected * Hover text on Input * Input html states, styling * Remove previous dest address settings state + component * Optimize OutputContianer address prop passed into DestinationAddressInput, remove previous component * Clean Bridge imports * Style Input * Replace useSelector with hook * clearDestinationAddress on Bridge Page load * Clean up Bridge reducer to not require actions file for better imports * Rm file * Maintain inputted destination address after bridge tx executes * Clear destination address by setting to null, clear input * Fix errors in BridgeTransactionButton * User inputted empty string throws error in input * Clean * Update text error state * Clean callbacks to make clear input flow clearer * Clean * Fix console errors * Clean * Add hover on warning buttnos * . * Refactor; clean functions * Use destination address as key * Style address dropdown * Rm log * Clean * Keep inputRef local * Add To * Dynamically set Input width * Adjust spacing for filled input * Dynamic input width including placeholder * Increase input width when focused * Remove shift when x button appears * Max width for input to fit on mobile * Clean * Basic Confirm Warning * Connect ConfirmWarning to store * Allow Warning text to be clickable * Style: BridgeTransactionButton Warning * Add setIsDestinationWarningAccepted to state to track status * Show Confirm warning button when warning not yet accepted + tracked in state * Conditions for showing Destination Address in Bridge Receipt * Remove prior destination warning popup * BridgeWarnings * Conditions to show Warning * Conditions to hide Destination Address Input * Simplify * Update button color for Confirm destination address * Use default text size for dropdown * Ensure blur from input on handleInputBlur * Ensure blur from input on handleInputBlur * .. * Add paste functionality * Conditions to show paste button * Add key press up/down direction for navigating list * Add pressing enter key functionality to select Address from list * Reset list idx onClose * Clean * Prevent input blur when mouseDown on ListReceipient item * Blur after selecting address from list * Update input ref to useRef * Fix merge imports * Make input smaller * Unconnected input padding * Remove shift when input is valid address * Add back SettingsSlideOver * Add back settings toggled page * Show destination address input when toggled on from Settings * Clean SettingsSlideOver * Clean * Remove Pink highlight on Input * Increase spacing between address and days ago * Increase width of input, limit for mobile * Remove yellow text in Warning * Only show Destination Address in Receipt if valid * Clear input if invalid and exists * Adjust dropdown spacing * Minor adjustments * Blur input after clearing * Enter button can submit input text for validation * Paste icon sizing * Show dest address for unconnected user if inputted * Hide destination address input on page load between page switch * Hide Bridge Warning if destination address is turned off * Ensure bridge listener clears destination address if destination input setting turned off * Remove unused code * Fix enter bug when wallet disconnected * Update dispatch value * Update to proper action to dispatch * Add back segment given we are using settings * Lint * Remove side effect to clear dest address * Origin token copy --------- Co-authored-by: abtestingalpha --- .../components/Portfolio/Portfolio.tsx | 3 +- .../BridgeExchangeRateInfo.tsx | 25 + .../BridgeTransactionButton.tsx | 39 +- .../StateManagedBridge/BridgeWarnings.tsx | 49 ++ .../DestinationAddressInput.tsx | 454 ++++++++++++++++-- .../StateManagedBridge/OutputContainer.tsx | 66 +-- .../StateManagedBridge/SettingsSlideOver.tsx | 82 +--- .../components/CloseButton.tsx | 12 +- .../components/buttons/SwitchButton.tsx | 2 +- .../components/ui/tailwind/Tooltip.tsx | 2 +- .../pages/state-managed-bridge/index.tsx | 126 +++-- .../slices/bridge/actions.ts | 12 - .../synapse-interface/slices/bridge/hooks.ts | 4 + .../slices/bridge/reducer.ts | 66 ++- .../slices/bridgeDisplaySlice.ts | 15 + .../utils/hooks/useAlternateBridgeQuotes.ts | 3 +- .../utils/hooks/useBridgeListener.ts | 4 +- .../synapse-interface/utils/isEmptyString.ts | 2 +- 18 files changed, 671 insertions(+), 295 deletions(-) create mode 100644 packages/synapse-interface/components/StateManagedBridge/BridgeWarnings.tsx delete mode 100644 packages/synapse-interface/slices/bridge/actions.ts diff --git a/packages/synapse-interface/components/Portfolio/Portfolio.tsx b/packages/synapse-interface/components/Portfolio/Portfolio.tsx index 1924aff061..99563277b9 100644 --- a/packages/synapse-interface/components/Portfolio/Portfolio.tsx +++ b/packages/synapse-interface/components/Portfolio/Portfolio.tsx @@ -21,8 +21,7 @@ import { import { resetTransactionsState } from '@/slices/transactions/actions' import { PortfolioState } from '@/slices/portfolio/reducer' import { useBridgeState } from '@/slices/bridge/hooks' -import { BridgeState } from '@/slices/bridge/reducer' -import { resetBridgeInputs } from '@/slices/bridge/actions' +import { BridgeState, resetBridgeInputs } from '@/slices/bridge/reducer' import { ViewSearchAddressBanner } from './components/ViewSearchAddressBanner' import { Activity } from '../Activity/Activity' import { useSearchInputState } from './helpers/useSearchInputStatus' diff --git a/packages/synapse-interface/components/StateManagedBridge/BridgeExchangeRateInfo.tsx b/packages/synapse-interface/components/StateManagedBridge/BridgeExchangeRateInfo.tsx index 2e21afa2df..765b645576 100644 --- a/packages/synapse-interface/components/StateManagedBridge/BridgeExchangeRateInfo.tsx +++ b/packages/synapse-interface/components/StateManagedBridge/BridgeExchangeRateInfo.tsx @@ -3,6 +3,7 @@ import Image from 'next/image' import { useMemo } from 'react' import { useAppSelector } from '@/store/hooks' import { useBridgeState } from '@/slices/bridge/hooks' +import { useAccount } from 'wagmi' import { useCoingeckoPrice } from '@hooks/useCoingeckoPrice' import { ELIGIBILITY_DEFAULT_TEXT, @@ -10,6 +11,7 @@ import { } from '@/utils/hooks/useStipEligibility' import { formatBigIntToString } from '@/utils/bigint/format' import { formatBigIntToPercentString } from '@/utils/bigint/format' +import { getValidAddress, isValidAddress } from '@/utils/isValidAddress' import { EMPTY_BRIDGE_QUOTE } from '@/constants/bridge' import { CHAINS_BY_ID } from '@constants/chains' import * as CHAINS from '@constants/chains/master' @@ -26,11 +28,34 @@ const BridgeExchangeRateInfo = () => { + ) } +const DestinationAddress = () => { + const { address } = useAccount() + const { destinationAddress } = useBridgeState() + + const showAddress = + destinationAddress && + getValidAddress(address) !== getValidAddress(destinationAddress) + + const isInputValidAddress: boolean = destinationAddress + ? isValidAddress(destinationAddress) + : false + + if (showAddress && isInputValidAddress) { + return ( +
+
To:
+
{destinationAddress}
+
+ ) + } +} + const Slippage = () => { const { fromValue, diff --git a/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx b/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx index a9c9e0ec9c..f5b52a1834 100644 --- a/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx +++ b/packages/synapse-interface/components/StateManagedBridge/BridgeTransactionButton.tsx @@ -9,9 +9,14 @@ import { isAddress } from 'viem' import { useConnectModal } from '@rainbow-me/rainbowkit' import { stringToBigInt } from '@/utils/bigint/format' -import { useBridgeState } from '@/slices/bridge/hooks' +import { useBridgeDisplayState, useBridgeState } from '@/slices/bridge/hooks' import { usePortfolioBalances } from '@/slices/portfolio/hooks' import { PAUSED_FROM_CHAIN_IDS, PAUSED_TO_CHAIN_IDS } from '@/constants/chains' +import { useAppDispatch } from '@/store/hooks' +import { + setIsDestinationWarningAccepted, + setShowDestinationWarning, +} from '@/slices/bridgeDisplaySlice' export const BridgeTransactionButton = ({ approveTxn, @@ -19,6 +24,7 @@ export const BridgeTransactionButton = ({ isApproved, isBridgePaused, }) => { + const dispatch = useAppDispatch() const [isConnected, setIsConnected] = useState(false) const { openConnectModal } = useConnectModal() @@ -45,10 +51,8 @@ export const BridgeTransactionButton = ({ isLoading, bridgeQuote, } = useBridgeState() - - const { showDestinationAddress } = useSelector( - (state: RootState) => state.bridgeDisplay - ) + const { showDestinationWarning, isDestinationWarningAccepted } = + useBridgeDisplayState() const balances = usePortfolioBalances() const balancesForChain = balances[fromChainId] @@ -69,7 +73,6 @@ export const BridgeTransactionButton = ({ bridgeQuote === EMPTY_BRIDGE_QUOTE_ZERO || bridgeQuote === EMPTY_BRIDGE_QUOTE || (destinationAddress && !isAddress(destinationAddress)) || - (showDestinationAddress && !destinationAddress) || (isConnected && !sufficientBalance) || PAUSED_FROM_CHAIN_IDS.includes(fromChainId) || PAUSED_TO_CHAIN_IDS.includes(toChainId) || @@ -109,7 +112,7 @@ export const BridgeTransactionButton = ({ } } else if (!fromToken) { buttonProperties = { - label: `Unsupported Network`, + label: `Please select an Origin token`, onClick: null, } } else if ( @@ -131,14 +134,16 @@ export const BridgeTransactionButton = ({ label: 'Insufficient balance', onClick: null, } - } else if (showDestinationAddress && !destinationAddress) { - buttonProperties = { - label: 'Please add valid destination address', - } } else if (destinationAddress && !isAddress(destinationAddress)) { buttonProperties = { label: 'Invalid destination address', } + } else if (showDestinationWarning && !isDestinationWarningAccepted) { + buttonProperties = { + label: 'Confirm destination address', + onClick: () => dispatch(setIsDestinationWarningAccepted(true)), + className: '!from-bgLight !to-bgLight', + } } else if (chain?.id != fromChainId && fromValueBigInt > 0) { buttonProperties = { label: `Switch to ${chains.find((c) => c.id === fromChainId)?.name}`, @@ -161,11 +166,13 @@ export const BridgeTransactionButton = ({ return ( buttonProperties && ( - + <> + + ) ) } diff --git a/packages/synapse-interface/components/StateManagedBridge/BridgeWarnings.tsx b/packages/synapse-interface/components/StateManagedBridge/BridgeWarnings.tsx new file mode 100644 index 0000000000..0cd92355b7 --- /dev/null +++ b/packages/synapse-interface/components/StateManagedBridge/BridgeWarnings.tsx @@ -0,0 +1,49 @@ +import { useAppDispatch } from '@/store/hooks' +import { useBridgeDisplayState } from '@/slices/bridge/hooks' +import { setIsDestinationWarningAccepted } from '@/slices/bridgeDisplaySlice' + +export const ConfirmDestinationAddressWarning = () => { + const dispatch = useAppDispatch() + const { + showDestinationWarning, + isDestinationWarningAccepted, + showDestinationAddress, + } = useBridgeDisplayState() + + const handleCheckboxChange = () => { + dispatch(setIsDestinationWarningAccepted(!isDestinationWarningAccepted)) + } + + if (showDestinationAddress && showDestinationWarning) { + return ( +
+ +
+

+ Required: Verify your destination address to + continue. +
+ Do not send assets to a custodial or exchange + address. It may be impossible to recover your funds. +

+
+
+ ) + } +} diff --git a/packages/synapse-interface/components/StateManagedBridge/DestinationAddressInput.tsx b/packages/synapse-interface/components/StateManagedBridge/DestinationAddressInput.tsx index 6169a066f6..99b314e13e 100644 --- a/packages/synapse-interface/components/StateManagedBridge/DestinationAddressInput.tsx +++ b/packages/synapse-interface/components/StateManagedBridge/DestinationAddressInput.tsx @@ -1,60 +1,432 @@ -import { setDestinationAddress } from '@/slices/bridge/reducer' -import { CHAINS_BY_ID } from '@constants/chains' -import { useDispatch } from 'react-redux' -import { Address } from 'wagmi' +import React, { useState, useRef, useEffect } from 'react' +import { isNull, isString } from 'lodash' +import { useAppDispatch } from '@/store/hooks' +import { isValidAddress } from '@/utils/isValidAddress' +import { shortenAddress } from '@/utils/shortenAddress' +import { useBridgeState, useBridgeDisplayState } from '@/slices/bridge/hooks' +import { + setDestinationAddress, + clearDestinationAddress, +} from '@/slices/bridge/reducer' +import { + setShowDestinationWarning, + setIsDestinationWarningAccepted, +} from '@/slices/bridgeDisplaySlice' +import { Address } from 'viem' +import { isEmptyString } from '@/utils/isEmptyString' +import { CloseButton } from './components/CloseButton' +import { useTransactionsState } from '@/slices/transactions/hooks' +import { TransactionsState } from '@/slices/transactions/reducer' +import { BridgeTransaction } from '@/slices/api/generated' +import { getValidAddress } from '@/utils/isValidAddress' +import { useKeyPress } from '@/utils/hooks/useKeyPress' +import useCloseOnOutsideClick from '@/utils/hooks/useCloseOnOutsideClick' export const DestinationAddressInput = ({ - toChainId, - destinationAddress, + connectedAddress, }: { - toChainId: number - destinationAddress: string + connectedAddress: string }) => { - const dispatch = useDispatch() - const chain = CHAINS_BY_ID[toChainId] - const chainName = chain?.name || 'nulls' + const dispatch = useAppDispatch() + const { destinationAddress } = useBridgeState() + const { showDestinationWarning } = useBridgeDisplayState() + const { userHistoricalTransactions }: TransactionsState = + useTransactionsState() + + const recipientList = filterTxsByRecipient( + userHistoricalTransactions, + connectedAddress + ) + const filteredRecipientList = filterNewestTxByRecipient(recipientList) + + const isInputValidAddress: boolean = destinationAddress + ? isValidAddress(destinationAddress) + : false + + const isInputInvalid: boolean = + (destinationAddress && + isString(destinationAddress) && + isEmptyString(destinationAddress)) || + (destinationAddress && !isInputValidAddress) + + const isSameAddress = + connectedAddress && + isInputValidAddress && + getValidAddress(destinationAddress) === getValidAddress(connectedAddress) + + useEffect(() => { + const showWarning = isInputValidAddress && !isSameAddress + + if (showWarning && !showDestinationWarning) { + dispatch(setShowDestinationWarning(true)) + } + + if (!isInputValidAddress && showDestinationWarning) { + dispatch(setShowDestinationWarning(false)) + dispatch(setIsDestinationWarningAccepted(false)) + } + }, [ + dispatch, + connectedAddress, + destinationAddress, + showDestinationWarning, + isInputValidAddress, + ]) + + const inputRef = useRef(null) + const [isInputFocused, setIsInputFocused] = useState(false) + + const handleInputFocus = () => { + setIsInputFocused(true) + setShowRecipientList(true) + + if (inputRef.current) { + inputRef.current.focus() + } + } + + const handleInputBlur = () => { + setIsInputFocused(false) + + if (inputRef.current) { + inputRef.current.blur() + } + + if (destinationAddress && isInputInvalid) { + handleClearInput() + } + } + + const handleClearInput = () => { + dispatch(clearDestinationAddress()) + + if (inputRef.current) { + inputRef.current.value = '' + } + } + + const onClearUserInput = () => { + handleClearInput() + handleInputBlur() + } + + useEffect(() => { + dispatch(clearDestinationAddress()) + handleClearInput() + }, [connectedAddress]) + + useEffect(() => { + if (!isInputFocused && isSameAddress) { + handleClearInput() + } + }, [isSameAddress, destinationAddress, connectedAddress, isInputFocused]) + let placeholder - placeholder = `Enter ${chainName} address...` + + if (isInputFocused) { + placeholder = '' + } else { + placeholder = connectedAddress ? shortenAddress(connectedAddress) : '0x...' + } + + let inputValue + + if (!destinationAddress) { + inputValue = '' + } else { + inputValue = + isInputValidAddress && !isInputFocused + ? shortenAddress(destinationAddress) + : destinationAddress + } + + const listRef = useRef(null) + const [showRecipientList, setShowRecipientList] = useState(false) + const [currentIdx, setCurrentIdx] = useState(null) + + const handleCloseList = () => { + if (showRecipientList) { + setShowRecipientList(false) + } + setCurrentIdx(null) + } + + const handlePaste = async () => { + const pastedValue = await navigator.clipboard.readText() + dispatch(setDestinationAddress(pastedValue as Address)) + } + + const onSelectRecipient = (address) => { + dispatch(setDestinationAddress(address)) + handleInputBlur() + handleCloseList() + } + + useCloseOnOutsideClick(listRef, handleCloseList) + + const escPressed = useKeyPress('Escape') + const arrowUp = useKeyPress('ArrowUp') + const arrowDown = useKeyPress('ArrowDown') + const enterPressed = useKeyPress('Enter') + + function escFunc() { + if (!showRecipientList) return + + if (escPressed) { + handleCloseList() + handleClearInput() + handleInputBlur() + } + } + + function arrowDownFunc() { + if (filteredRecipientList.length === 0) return + if (!showRecipientList) return + + const nextIdx = currentIdx + 1 + if (currentIdx === null) { + setCurrentIdx(0) + } else if (arrowDown && nextIdx < filteredRecipientList.length) { + setCurrentIdx(nextIdx) + } + } + + function arrowUpFunc() { + if (filteredRecipientList.length === 0) return + if (!showRecipientList) return + + const nextIdx = currentIdx - 1 + + if (arrowUp && -1 < nextIdx) { + setCurrentIdx(nextIdx) + } + } + + function enterPressedFunc() { + if (enterPressed && isNull(currentIdx)) { + onSelectRecipient(destinationAddress) + } + + if (enterPressed && !isNull(currentIdx) && currentIdx > -1) { + onSelectRecipient(filteredRecipientList[currentIdx]?.toAddress) + } + } + + useEffect(enterPressedFunc, [enterPressed]) + useEffect(escFunc, [escPressed]) + useEffect(arrowDownFunc, [arrowDown]) + useEffect(arrowUpFunc, [arrowUp]) + + const adjustInputSize = () => { + const addressInput: HTMLElement = document.getElementById('address-input') + + if (isInputFocused || isInputInvalid) { + addressInput.style.width = '12rem' + } else if (inputValue.length > 0) { + addressInput.style.width = inputValue.length + 2 + 'ch' + } else { + addressInput.style.width = placeholder.length + 'ch' + } + } + + useEffect(() => { + adjustInputSize() + }, [inputValue, placeholder, isInputFocused, showRecipientList]) return ( -
-
-
- Withdraw to... -
-
-
+
+
+
To:
+ dispatch(setDestinationAddress(e.target.value as Address)) + } + onFocus={handleInputFocus} + onBlur={handleInputBlur} + placeholder={placeholder} + value={inputValue} className={` - focus:outline-none - focus:ring-0 - focus:border-none - border-none - bg-transparent - w-[300px] - sm:min-w-[400px] - max-w-[calc(100%-92px)] - sm:w-full - text-white text-opacity-80 text-lg - placeholder:text-[#88818C] + transform-gpu transition-all duration-175 cursor-pointer max-w-36 md:max-w-48 + text-sm rounded-sm text-strong py-0.5 pl-1.5 z-0 border-0 bg-transparent + focus:text-white focus:border-transparent focus:outline-none focus:ring-0 + ${isInputFocused || isInputInvalid ? 'text-left ' : 'text-center'} + ${destinationAddress ? 'pr-6' : 'pr-1.5'} `} - placeholder={placeholder} - onChange={(e) => { - dispatch(setDestinationAddress(e.target.value as Address)) - }} - value={destinationAddress} /> + {destinationAddress ? ( + + ) : isInputFocused ? ( + + ) : null}
+
+ {showRecipientList && ( +
    + {filteredRecipientList?.map((recipient, index) => { + return ( + + ) + })} +
+ )} +
+
+ ) +} + +const ListRecipient = ({ + index, + address, + daysAgo, + selectedListItem, + onSelectRecipient, +}: { + index: number + address: string + daysAgo: number + selectedListItem: number | null + onSelectRecipient?: (destinationAddress: Address) => void +}) => { + const handleMouseDown = (event: React.MouseEvent) => { + event.preventDefault() + onSelectRecipient && onSelectRecipient(address as Address) + } + + return ( +
+
{shortenAddress(address)}
+
{daysAgo}d
) } + +const filterTxsByRecipient = ( + transactions: BridgeTransaction[], + connectedAddress?: string +): { + toAddress: string | undefined + time: string | undefined + daysAgo: number +}[] => { + const checkAddress = getValidAddress(connectedAddress) + return transactions + ?.filter( + (transaction) => + getValidAddress(transaction.toInfo?.address) !== checkAddress + ) + .map((transaction) => ({ + toAddress: getValidAddress(transaction.toInfo?.address), + time: transaction.toInfo?.formattedTime, + daysAgo: calculateDaysAgo(transaction.toInfo?.formattedTime), + })) +} + +const filterNewestTxByRecipient = ( + transactions: { + toAddress: string | undefined + time: string | undefined + daysAgo: number + }[] +) => { + const newestTxMap = new Map() + + transactions?.forEach((tx) => { + const existingTx = newestTxMap.get(tx.toAddress) + + if (!existingTx || tx.daysAgo < existingTx.daysAgo) { + newestTxMap.set(tx.toAddress, tx) + } + }) + + return Array.from(newestTxMap.values()) +} + +const calculateDaysAgo = (time?: string) => { + if (time) { + // Assuming timeString is in "YYYY-MM-DD HH:MM:SS +0000 UTC" format + const formattedTimeString = time.replace(' +0000 UTC', 'Z') + + const timeDate = new Date(formattedTimeString) + const now = new Date() + + return calculateDaysBetween(timeDate, now) + } else { + return null + } +} + +const calculateDaysBetween = (startDate: Date, endDate: Date) => { + const msPerDay = 1000 * 60 * 60 * 24 + const utc1 = Date.UTC( + startDate.getFullYear(), + startDate.getMonth(), + startDate.getDate() + ) + const utc2 = Date.UTC( + endDate.getFullYear(), + endDate.getMonth(), + endDate.getDate() + ) + + return Math.floor((utc2 - utc1) / msPerDay) +} + +const PasteButton = ({ onPaste }: { onPaste: () => Promise }) => { + return ( + e.preventDefault()} + className={` + absolute border-transparent cursor-pointer + right-2.5 mt-0.5 justify-self-end + fill-zinc-100 stroke-zinc-100 hover:opacity-70 + `} + > + + + + ) +} diff --git a/packages/synapse-interface/components/StateManagedBridge/OutputContainer.tsx b/packages/synapse-interface/components/StateManagedBridge/OutputContainer.tsx index 01045c145a..a923ccfad8 100644 --- a/packages/synapse-interface/components/StateManagedBridge/OutputContainer.tsx +++ b/packages/synapse-interface/components/StateManagedBridge/OutputContainer.tsx @@ -1,47 +1,30 @@ -import { useEffect, useState } from 'react' -import { Address, useAccount } from 'wagmi' - -import LoadingDots from '../ui/tailwind/LoadingDots' +import { useAccount } from 'wagmi' import { ToChainSelector } from './ToChainSelector' -import { shortenAddress } from '@/utils/shortenAddress' import { ToTokenSelector } from './ToTokenSelector' -import { useDispatch } from 'react-redux' -import { setToChainId, setToToken } from '@/slices/bridge/reducer' -import { useBridgeState } from '@/slices/bridge/hooks' +import { useBridgeState, useBridgeDisplayState } from '@/slices/bridge/hooks' +import { DestinationAddressInput } from './DestinationAddressInput' +import LoadingDots from '../ui/tailwind/LoadingDots' export const OutputContainer = ({}) => { - const { bridgeQuote, isLoading, toChainId, toToken } = useBridgeState() - - const { address: isConnectedAddress } = useAccount() - const [address, setAddress] = useState
() - - const dispatch = useDispatch() - - useEffect(() => { - setAddress(isConnectedAddress) - }, [isConnectedAddress]) - - // update address for destination address if we have a destination address + const { address } = useAccount() + const { bridgeQuote, isLoading } = useBridgeState() + const { showDestinationAddress } = useBridgeDisplayState() return ( -
+
- {/* {address && ( -
- -
- )} */} + + {showDestinationAddress ? ( + + ) : null}
@@ -53,15 +36,10 @@ export const OutputContainer = ({}) => { pattern="[0-9.]+" disabled={true} className={` - focus:outline-none - focus:ring-0 - focus:border-none - border-none - p-0 - bg-transparent - max-w-[190px] - placeholder:text-[#88818C] - text-white text-opacity-80 text-xl md:text-2xl font-medium + text-white text-opacity-80 text-xl font-medium + border-none p-0 bg-transparent + focus:outline-none focus:ring-0 focus:border-none + max-w-[190px] md:text-2xl placeholder:text-[#88818C] `} placeholder="0.0000" value={ @@ -79,11 +57,3 @@ export const OutputContainer = ({}) => {
) } - -const DisplayAddress = ({ address }) => { - return ( -
- {shortenAddress(address)} -
- ) -} diff --git a/packages/synapse-interface/components/StateManagedBridge/SettingsSlideOver.tsx b/packages/synapse-interface/components/StateManagedBridge/SettingsSlideOver.tsx index 48d803cf2c..d473cfd42f 100644 --- a/packages/synapse-interface/components/StateManagedBridge/SettingsSlideOver.tsx +++ b/packages/synapse-interface/components/StateManagedBridge/SettingsSlideOver.tsx @@ -1,12 +1,10 @@ import _ from 'lodash' import { useEffect } from 'react' -import { useDispatch, useSelector } from 'react-redux' +import { useAppDispatch } from '@/store/hooks' import { InformationCircleIcon } from '@heroicons/react/outline' +import Tooltip from '@tw/Tooltip' import { Switch } from '@headlessui/react' import { useKeyPress } from '@hooks/useKeyPress' -import Tooltip from '@tw/Tooltip' -import Button from '@tw/Button' - import { setShowDestinationAddress, setShowSettingsSlideOver, @@ -15,21 +13,19 @@ import { setDeadlineMinutes, setDestinationAddress, } from '@/slices/bridge/reducer' -import { RootState } from '@/store/store' +import { useBridgeDisplayState } from '@/slices/bridge/hooks' const SettingsSlideOver = () => { - const dispatch = useDispatch() - const escPressed = useKeyPress('Escape') + const dispatch = useAppDispatch() + const { showDestinationAddress } = useBridgeDisplayState() - const { showDestinationAddress } = useSelector( - (state: RootState) => state.bridgeDisplay - ) - - function onClose() { + const onClose = () => { dispatch(setShowSettingsSlideOver(false)) } - function escFunc() { + const escPressed = useKeyPress('Escape') + + const escFunc = () => { if (escPressed) { onClose() } @@ -41,9 +37,8 @@ const SettingsSlideOver = () => {
@@ -62,20 +57,21 @@ const SettingsSlideOver = () => { onChange={(selected: boolean) => { if (selected) { dispatch(setShowDestinationAddress(true)) + onClose() } else { dispatch(setShowDestinationAddress(false)) dispatch(setDestinationAddress(null)) } }} className={` - bg-gradient-to-r + relative inline-flex items-center h-6 rounded-full w-11 + transition-colors bg-gradient-to-r focus:outline-none ${ showDestinationAddress ? ' from-[#FF00FF] to-[#AC8FFF]' : 'from-gray-900 to-gray-900' } - relative inline-flex items-center h-6 rounded-full w-11 - transition-colors focus:outline-none`} + `} > {
- {showDestinationAddress && } -
-
- ) -} - -const WithdrawalWarning = ({ onClose }: { onClose: any }) => { - return ( -
-
-
- Do not send your funds to a custodial wallet or exchange address!{' '} - - It may be impossible to recover your funds. - -
-
) } const DeadlineInput = ({ deadlineMinutes }: { deadlineMinutes: number }) => { - const dispatch = useDispatch() + const dispatch = useAppDispatch() return (
dispatch(setDeadlineMinutes(Number(e.target.value)))} diff --git a/packages/synapse-interface/components/StateManagedBridge/components/CloseButton.tsx b/packages/synapse-interface/components/StateManagedBridge/components/CloseButton.tsx index c9a0c25099..17169fe809 100644 --- a/packages/synapse-interface/components/StateManagedBridge/components/CloseButton.tsx +++ b/packages/synapse-interface/components/StateManagedBridge/components/CloseButton.tsx @@ -1,13 +1,19 @@ import { XIcon } from '@heroicons/react/outline' -export const CloseButton = ({ onClick }: { onClick: () => void }) => { +export const CloseButton = ({ + onClick, + className, +}: { + onClick: () => void + className?: string +}) => { return (