-
Notifications
You must be signed in to change notification settings - Fork 26
feat: implement Buy G$ page with 3-step progress bar and responsive Onramper widget #605
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 16 commits
6004194
ad8846f
3e16bb0
f39e512
9b2be7e
2c71b08
27225ef
c2d859b
4eb0910
54c30ed
655c4c5
d75cfa5
90e423a
0edd742
aa1dfac
42994ce
16768e3
4b4fd71
2d9cfb6
2410d7d
c33295d
e96a55f
41e4994
6c842be
2074013
9431ac8
87d5232
7363f3e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,177 @@ | ||
| import React, { useEffect, useState } from 'react' | ||
| import { Box, HStack, Circle, Text } from 'native-base' | ||
|
|
||
| export type BuyStep = 1 | 2 | 3 | ||
|
|
||
| interface BuyProgressBarProps { | ||
| currentStep: BuyStep | ||
| isLoading?: boolean | ||
| } | ||
|
|
||
| const BuyProgressBar: React.FC<BuyProgressBarProps> = ({ currentStep, isLoading = false }) => { | ||
| const [animatedWidth, setAnimatedWidth] = useState(0) | ||
|
|
||
| const steps = [ | ||
| { number: 1, label: 'Buy cUSD' }, | ||
| { number: 2, label: 'We swap cUSD to G$' }, | ||
| { number: 3, label: 'Done' }, | ||
| ] | ||
|
||
|
|
||
| // Handle animated progress line | ||
| useEffect(() => { | ||
| if (isLoading && currentStep > 1) { | ||
| // Animate progress line when loading | ||
| let progress = 0 | ||
| const interval = setInterval(() => { | ||
| progress += 2 | ||
| if (progress <= 100) { | ||
| setAnimatedWidth(progress) | ||
| } else { | ||
| clearInterval(interval) | ||
| } | ||
| }, 50) // 50ms intervals for smooth animation | ||
|
|
||
| return () => clearInterval(interval) | ||
| } else { | ||
| // Set to 100% if not loading (completed state) | ||
| setAnimatedWidth(100) | ||
| } | ||
| }, [isLoading, currentStep]) | ||
supersonicwisd1 marked this conversation as resolved.
Show resolved
Hide resolved
Comment on lines
29
to
49
This comment was marked as resolved.
Sorry, something went wrong. |
||
|
|
||
| const getStepStatus = (stepNumber: number) => { | ||
sourcery-ai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| // Step 1 should ALWAYS be blue (active when current, completed when past) | ||
| if (stepNumber === 1) { | ||
| if (currentStep === 1) { | ||
| return isLoading ? 'loading' : 'active' | ||
| } else { | ||
| return 'completed' // Step 1 is completed when we're on step 2 or 3 | ||
| } | ||
| } | ||
| // Steps 2 and 3 follow normal logic | ||
| if (stepNumber < currentStep) return 'completed' | ||
| if (stepNumber === currentStep) return isLoading ? 'loading' : 'active' | ||
| return 'pending' | ||
| } | ||
|
|
||
| const getCircleProps = (status: string) => { | ||
| const baseProps = { | ||
| size: '12', | ||
| mb: 2, | ||
| justifyContent: 'center', | ||
| alignItems: 'center', | ||
| } | ||
|
|
||
| switch (status) { | ||
| case 'completed': | ||
supersonicwisd1 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| return { ...baseProps, bg: 'blue.500' } | ||
| case 'active': | ||
| return { ...baseProps, bg: 'blue.500' } | ||
| case 'loading': | ||
| return { | ||
| ...baseProps, | ||
| bg: 'blue.500', | ||
| borderWidth: 3, | ||
| borderColor: 'blue.200', | ||
| animation: 'pulse 2s infinite', | ||
| } | ||
| default: | ||
| return { ...baseProps, bg: 'gray.300' } | ||
| } | ||
| } | ||
|
||
|
|
||
| const getLineProps = (stepNumber: number, lineIndex: number) => { | ||
| // Line between step 1 and 2 (lineIndex = 0) | ||
| if (lineIndex === 0) { | ||
| if (currentStep === 1 && isLoading) { | ||
| // Animation state: "1 Blue with progress bar animation" | ||
| return { | ||
| bg: 'blue.500', | ||
| width: `${animatedWidth}%`, | ||
| transition: 'width 0.1s ease-out', | ||
| } | ||
| } else if (currentStep >= 2) { | ||
| // Static line when step 2 or higher | ||
| return { | ||
| bg: 'blue.500', | ||
| width: '100%', | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // Line between step 2 and 3 (lineIndex = 1) | ||
| if (lineIndex === 1) { | ||
| if (currentStep === 2 && isLoading) { | ||
| // Animation state: "2 Blue with progress bar animation" | ||
| return { | ||
| bg: 'blue.500', | ||
| width: `${animatedWidth}%`, | ||
| transition: 'width 0.1s ease-out', | ||
| } | ||
| } else if (currentStep >= 3) { | ||
| // Static line when step 3 | ||
| return { | ||
| bg: 'blue.500', | ||
| width: '100%', | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // Default: gray line (not active) | ||
| return { | ||
| bg: 'gray.300', | ||
| width: '100%', | ||
| } | ||
| } | ||
|
|
||
| const getTextColor = (status: string) => { | ||
| return status === 'pending' ? 'gray.500' : 'black' | ||
| } | ||
|
|
||
| return ( | ||
| <Box width="100%" mb={6} mt={4} data-testid="custom-progress-bar"> | ||
| <HStack justifyContent="space-between" alignItems="flex-start" position="relative"> | ||
| {steps.map((step, index) => { | ||
| const status = getStepStatus(step.number) | ||
|
|
||
| return ( | ||
| <React.Fragment key={step.number}> | ||
| <Box alignItems="center" flex={1} position="relative"> | ||
| <Circle {...getCircleProps(status)}> | ||
| <Text color="white" fontWeight="bold" fontSize="md"> | ||
| {step.number} | ||
| </Text> | ||
| </Circle> | ||
| <Text | ||
| textAlign="center" | ||
| fontSize="sm" | ||
| color={getTextColor(status)} | ||
| fontFamily="subheading" | ||
| maxWidth="120px" | ||
| lineHeight="tight" | ||
| > | ||
| {step.label} | ||
| </Text> | ||
| </Box> | ||
|
|
||
| {index < steps.length - 1 && ( | ||
| <Box | ||
| position="absolute" | ||
| top="6" | ||
| left={`${33.33 * (index + 1) - 16.67}%`} | ||
| right={`${66.67 - 33.33 * (index + 1) + 16.67}%`} | ||
| height="2px" | ||
| bg="gray.300" | ||
| zIndex={-1} | ||
| > | ||
| <Box height="100%" {...getLineProps(step.number + 1, index)} borderRadius="1px" /> | ||
| </Box> | ||
| )} | ||
| </React.Fragment> | ||
| ) | ||
| })} | ||
| </HStack> | ||
| </Box> | ||
| ) | ||
| } | ||
|
|
||
| export { BuyProgressBar } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,141 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import React, { useCallback, useEffect, useRef, useState } from 'react' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { useEthers, useEtherBalance, useTokenBalance } from '@usedapp/core' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { WebViewMessageEvent } from 'react-native-webview' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { AsyncStorage, useBuyGd } from '@gooddollar/web3sdk-v2' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { noop } from 'lodash' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { useModal } from '@gooddollar/good-design/dist/hooks/useModal' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { View, Text } from 'native-base' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { WalletAndChainGuard } from '@gooddollar/good-design' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { useSignWalletModal } from '@gooddollar/good-design/dist/hooks/useSignWalletModal' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { CustomOnramper } from './CustomOnramper' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const ErrorModal = () => ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <View> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <Text>Something went wrong.</Text> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </View> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| interface ICustomGdOnramperProps { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onEvents: (action: string, data?: any, error?: string) => void | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| selfSwap?: boolean | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| withSwap?: boolean | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| donateOrExecTo?: string | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| callData?: string | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| apiKey?: string | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export const CustomGdOnramperWidget = ({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export const CustomGdOnramperWidget = ({ | |
| // 1) Extract the Onramper iframe logic into a `useOnramper` hook. | |
| // src/hooks/useOnramper.ts | |
| import { useCallback, useState } from 'react' | |
| import { WebViewMessageEvent } from 'react-native-webview' | |
| import { AsyncStorage } from '@gooddollar/web3sdk-v2' | |
| export function useOnramper(onEvents: (action: string, data?: any, error?: string) => void) { | |
| const [step, setStep] = useState(0) | |
| const onEvent = useCallback(async (event: WebViewMessageEvent) => { | |
| try { | |
| const data = | |
| typeof event.nativeEvent.data === 'string' | |
| ? JSON.parse(event.nativeEvent.data) | |
| : event.nativeEvent.data | |
| if (data?.title === 'success') { | |
| await AsyncStorage.setItem('gdOnrampSuccess', 'true') | |
| setStep(2) | |
| onEvents('onramp_success') | |
| } | |
| } catch { | |
| /* ignore */ | |
| } | |
| }, [onEvents]) | |
| return { step, setStep, onEvent } | |
| } | |
| // 2) Extract the swap-flow & lock logic into `useSwapFlow`. | |
| // src/hooks/useSwapFlow.ts | |
| import { useEffect, useRef } from 'react' | |
| import { useBuyGd } from '@gooddollar/web3sdk-v2' | |
| interface SwapParams { | |
| selfSwap: boolean | |
| withSwap: boolean | |
| donateOrExecTo?: string | |
| callData: string | |
| account?: string | |
| library?: any | |
| gdHelperAddress?: string | |
| onEvents: (action: string, data?: any, error?: string) => void | |
| showError: () => void | |
| setStep: (step: number) => void | |
| } | |
| export function useSwapFlow({ | |
| selfSwap, | |
| withSwap, | |
| donateOrExecTo, | |
| callData, | |
| account, | |
| library, | |
| gdHelperAddress, | |
| onEvents, | |
| showError, | |
| setStep, | |
| }: SwapParams) { | |
| const swapLock = useRef(false) | |
| const { createAndSwap, swap, swapState, createState, triggerSwapTx } = useBuyGd({ | |
| donateOrExecTo, | |
| callData, | |
| withSwap, | |
| }) | |
| const triggerSwap = async () => { | |
| if (swapLock.current) return | |
| swapLock.current = true | |
| try { | |
| setStep(3) | |
| let txPromise | |
| if (selfSwap && gdHelperAddress && library && account) { | |
| const code = await library.getCode(gdHelperAddress) | |
| txPromise = | |
| code.length <= 2 ? createAndSwap(0 /* oracle min */) : swap(0) | |
| } else { | |
| setStep(4) | |
| txPromise = triggerSwapTx() | |
| } | |
| const res = await txPromise | |
| if (res?.status !== 1 && !res?.ok) throw new Error('swap failed') | |
| setStep(5) | |
| onEvents('buy_success') | |
| } catch (e: any) { | |
| showError() | |
| onEvents('buygd_swap_failed', e.message) | |
| setStep(0) | |
| } finally { | |
| swapLock.current = false | |
| } | |
| } | |
| return { swapState, createState, triggerSwap } | |
| } | |
| // 3) In your component, wire them together: | |
| import { useOnramper } from 'src/hooks/useOnramper' | |
| import { useSwapFlow } from 'src/hooks/useSwapFlow' | |
| export const CustomGdOnramperWidget = (props: ICustomGdOnramperProps) => { | |
| const { onEvents, selfSwap, withSwap, donateOrExecTo, callData, apiKey } = props | |
| const { account, library } = useEthers() | |
| const { showModal, Modal } = useModal() | |
| const { SignWalletModal } = useSignWalletModal() | |
| // 3.a Onramper | |
| const { step, setStep, onEvent } = useOnramper(onEvents) | |
| // 3.b Swap logic | |
| const { swapState, createState, triggerSwap } = useSwapFlow({ | |
| selfSwap, | |
| withSwap, | |
| donateOrExecTo, | |
| callData, | |
| account, | |
| library, | |
| gdHelperAddress, | |
| onEvents, | |
| showError: () => showModal(), | |
| setStep, | |
| }) | |
| // 3.c Balance watch to auto-trigger | |
| useEffect(() => { | |
| if ((cusdBalance?.gt(0) || celoBalance?.gt(0))) { | |
| AsyncStorage.removeItem('gdOnrampSuccess') | |
| triggerSwap() | |
| } | |
| }, [cusdBalance, celoBalance]) | |
| return ( | |
| <> | |
| <Modal body={<ErrorModal />} /> | |
| <WalletAndChainGuard validChains={[42220]}> | |
| <CustomOnramper | |
| onEvent={onEvent} | |
| targetWallet={gdHelperAddress || ''} | |
| step={step} | |
| setStep={setStep} | |
| targetNetwork="CELO" | |
| widgetParams={{ onlyCryptos: 'CUSD_CELO', isAddressEditable: false }} | |
| onGdEvent={onEvents} | |
| apiKey={apiKey} | |
| /> | |
| </WalletAndChainGuard> | |
| {/* single SignWalletModal covering both statuses */} | |
| <SignWalletModal txStatuses={[swapState.status, createState.status]} /> | |
| </> | |
| ) | |
| } |
This splits the iframe parsing, AsyncStorage flag, swap logic, and modal orchestration into small focused hooks/components—maintaining all existing behavior while significantly flattening and decluttering the main component.
Outdated
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issue (complexity): Consider refactoring the component to use style maps and CSS transitions instead of manual state and interval logic for progress animation.
You can collapse most of the branching into simple style-maps and drop the manual
setIntervalentirely by using CSS transitions onwidth. For example:This:
animatedWidthstate + interval.getStepStatusso logic stays intact.