diff --git a/packages/atlas/src/components/ChangeNowModal/ChangeNowModal.tsx b/packages/atlas/src/components/ChangeNowModal/ChangeNowModal.tsx
index 225aafdca5..e6cb986d3b 100644
--- a/packages/atlas/src/components/ChangeNowModal/ChangeNowModal.tsx
+++ b/packages/atlas/src/components/ChangeNowModal/ChangeNowModal.tsx
@@ -4,6 +4,7 @@ import { useCallback, useLayoutEffect, useMemo, useRef, useState } from 'react'
import { SvgAlertsInformative32, SvgLogoChangenow } from '@/assets/icons'
import { FailedStep } from '@/components/ChangeNowModal/steps/FailedStep'
import { SwapExpired } from '@/components/ChangeNowModal/steps/SwapExpired'
+import { TransactionTimeout } from '@/components/ChangeNowModal/steps/TransactionTimeout'
import { Spinner } from '@/components/_loaders/Spinner'
import { DialogButtonProps } from '@/components/_overlays/Dialog'
import { DialogModal } from '@/components/_overlays/DialogModal'
@@ -61,7 +62,14 @@ export const ChangeNowModal = ({ type, onClose }: ChangeNowModalProps) => {
}, [step, type])
const secondaryButton = useMemo(() => {
- if ([ChangeNowModalStep.INFO, ChangeNowModalStep.SWAP_EXPIRED, ChangeNowModalStep.FAILED].includes(step)) {
+ if (
+ [
+ ChangeNowModalStep.INFO,
+ ChangeNowModalStep.SWAP_EXPIRED,
+ ChangeNowModalStep.TIMEOUT,
+ ChangeNowModalStep.FAILED,
+ ].includes(step)
+ ) {
return {
text: 'Cancel',
onClick: () => onClose(),
@@ -110,8 +118,10 @@ export const ChangeNowModal = ({ type, onClose }: ChangeNowModalProps) => {
+ [ChangeNowModalStep.SWAP_EXPIRED, ChangeNowModalStep.TIMEOUT, ChangeNowModalStep.FAILED].includes(step) ? (
+
) : type === 'sell' ? (
'Cashout JOY'
) : (
@@ -119,7 +129,14 @@ export const ChangeNowModal = ({ type, onClose }: ChangeNowModalProps) => {
)
}
show
- dividers={![ChangeNowModalStep.INFO, ChangeNowModalStep.SWAP_EXPIRED, ChangeNowModalStep.FAILED].includes(step)}
+ dividers={
+ ![
+ ChangeNowModalStep.INFO,
+ ChangeNowModalStep.SWAP_EXPIRED,
+ ChangeNowModalStep.FAILED,
+ ChangeNowModalStep.TIMEOUT,
+ ].includes(step)
+ }
onExitClick={step === ChangeNowModalStep.SWAP_EXPIRED ? undefined : () => onClose()}
primaryButton={
primaryButtonProps
@@ -142,6 +159,9 @@ export const ChangeNowModal = ({ type, onClose }: ChangeNowModalProps) => {
)}
{step === ChangeNowModalStep.SWAP_EXPIRED && }
+ {step === ChangeNowModalStep.TIMEOUT && transactionData.current && (
+
+ )}
{step === ChangeNowModalStep.FAILED && transactionData.current && (
)}
diff --git a/packages/atlas/src/components/ChangeNowModal/steps/ProgressStep.tsx b/packages/atlas/src/components/ChangeNowModal/steps/ProgressStep.tsx
index a077e78ca6..84ea80a374 100644
--- a/packages/atlas/src/components/ChangeNowModal/steps/ProgressStep.tsx
+++ b/packages/atlas/src/components/ChangeNowModal/steps/ProgressStep.tsx
@@ -1,6 +1,6 @@
import styled from '@emotion/styled'
import QRCode from 'qrcode.react'
-import { useMemo, useState } from 'react'
+import { useMemo, useRef, useState } from 'react'
import { useQuery } from 'react-query'
import { SvgActionChevronR, SvgAlertsSuccess24 } from '@/assets/icons'
@@ -64,6 +64,7 @@ export const ProgressStep = ({
const steps = isSellingJoy ? sellSteps : buySteps
const { trackChangenowTokenSold, trackChangenowTokenBought } = useSegmentAnalytics()
const { memberId } = useUser()
+ const mountTimestamp = useRef(Date.now())
const { data } = useQuery(
['getTransactionStatus', transactionData.id],
() => changeNowService.getTransactionStatus(transactionData.id).then((res) => res.data),
@@ -73,6 +74,12 @@ export const ProgressStep = ({
if (data.status === 'failed') {
goToStep(ChangeNowModalStep.FAILED)
}
+
+ // if transaction doesn't fail or succeed after 26 min
+ // the issue might be on API side, we should not waste more time waiting
+ if (retry && Date.now() - mountTimestamp.current > 26 * 60 * 1000) {
+ goToStep(ChangeNowModalStep.TIMEOUT)
+ }
},
}
)
@@ -160,6 +167,7 @@ export const ProgressStep = ({
hideStepNumberText={currentStep !== idx}
title={currentStep === idx ? stepText : ''}
variant={currentStep < idx ? 'future' : currentStep === idx ? 'current' : 'completed'}
+ showOtherStepsOnMobile
/>
>
))}
@@ -172,10 +180,7 @@ export const ProgressStep = ({
Exchange ID:
- window.open(`https://changenow.io/exchange/txs/${transactionData.id}`, '_blank')}
- >
+
{transactionData.id}
diff --git a/packages/atlas/src/components/ChangeNowModal/steps/TransactionTimeout.tsx b/packages/atlas/src/components/ChangeNowModal/steps/TransactionTimeout.tsx
new file mode 100644
index 0000000000..f1441908bc
--- /dev/null
+++ b/packages/atlas/src/components/ChangeNowModal/steps/TransactionTimeout.tsx
@@ -0,0 +1,22 @@
+import { FlexBox } from '@/components/FlexBox'
+import { Text } from '@/components/Text'
+import { TextButton } from '@/components/_buttons/Button'
+import { atlasConfig } from '@/config'
+
+type TransactionTimeoutProps = {
+ transactionId: string
+}
+export const TransactionTimeout = ({ transactionId }: TransactionTimeoutProps) => {
+ return (
+
+
+ Transaction timed out
+
+
+ {atlasConfig.general.appName} did not receive any transaction status update from ChangeNOW for 25 minutes.
+ Transaction might have succeeded and we just don't know about it. Double check transaction status on ChangeNOW
+ site by clicking this button.
+
+
+ )
+}
diff --git a/packages/atlas/src/components/ChangeNowModal/steps/types.ts b/packages/atlas/src/components/ChangeNowModal/steps/types.ts
index 21c3a16226..7e7c63e954 100644
--- a/packages/atlas/src/components/ChangeNowModal/steps/types.ts
+++ b/packages/atlas/src/components/ChangeNowModal/steps/types.ts
@@ -6,6 +6,7 @@ export enum ChangeNowModalStep {
SUMMARY,
PROGRESS,
SWAP_EXPIRED,
+ TIMEOUT,
FAILED,
}