Skip to content
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

🧑‍🚀 POC Guadarian #6459

Open
wants to merge 8 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/atlas/src/.env
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ VITE_ASSET_LOGS_URL=
VITE_GEOLOCATION_SERVICE_URL=https://geolocation.joystream.org
VITE_HCAPTCHA_SITE_KEY=41cae189-7676-4f6b-aa56-635be26d3ceb
VITE_CHANGENOW_PUBLIC_API_KEY=0d8a58104f82b860a70e9460bccf62ae1e0fca4a93fd7e0c27c90448187b988f
VITE_GUARDARIAN_PUBLIC_API_KEY=8f6319f6-001e-40c1-b772-ad18dd7066c0
VITE_WALLET_CONNECT_PROJECT_ID=33b2609463e399daee8c51726546c8dd

# YPP configuration
Expand Down
21 changes: 12 additions & 9 deletions packages/atlas/src/components/ChangeNowModal/steps/FormStep.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import { useSubscribeAccountBalance } from '@/providers/joystream'
import { useSnackbar } from '@/providers/snackbars'
import { square } from '@/styles'
import {
Currency,
JOYSTREAM_CHANGENOW_LEGACY_TICKER,
JOYSTREAM_CHANGENOW_TICKER,
changeNowService,
Expand Down Expand Up @@ -339,24 +338,28 @@ export const FormStep = ({ setPrimaryButtonProps, onSubmit, type, initialValues
)
}

type CurrencyInputProps = {
currencies: ComboBoxProps<{ value: string } & Currency>['items']
initialCurrency?: ComboBoxProps<{ value: string } & Currency>['initialSelectedItem']
type CurrencyInputProps<T> = {
currencies: ComboBoxProps<{ value: string } & T>['items']
initialCurrency?: ComboBoxProps<{ value: string } & T>['initialSelectedItem']
currency?: string
lockedCurrency?: string
onCurrencySelect: (value: string) => void
isLoading?: boolean
} & TokenInputProps

const CurrencyInput = ({
export const CurrencyInput = <T extends { value: string }>({
currencies,
onCurrencySelect,
lockedCurrency,
isLoading,
currency,
initialCurrency,
...tokenProps
}: CurrencyInputProps) => {
}: CurrencyInputProps<T>) => {
const [sel, setSel] = useState<string | undefined>(initialCurrency?.value)
const selected = currencies?.find((opt) => opt.value.toLowerCase() === (lockedCurrency ?? sel)?.toLowerCase())
const selected = currencies?.find(
(opt) => opt.value.toLowerCase() === (lockedCurrency ?? currency ?? sel)?.toLowerCase()
)
return (
<InputGrid>
<TokenInput {...tokenProps} nodeStart={<div />} nodeEnd={isLoading ? <StyledSpinner /> : <div />} />
Expand All @@ -368,8 +371,8 @@ const CurrencyInput = ({
disabled={!!lockedCurrency}
placeholder="BTC"
onSelectedItemChange={(e) => {
setSel(e?.legacyTicker ?? '')
onCurrencySelect(e?.legacyTicker ?? '')
setSel(e?.value ?? '')
onCurrencySelect(e?.value ?? '')
}}
items={currencies}
/>
Expand Down
144 changes: 144 additions & 0 deletions packages/atlas/src/components/GuardarianModal/GuardarianModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import styled from '@emotion/styled'
import { useRef, useState } from 'react'
import ReactDOM from 'react-dom'

import { SvgActionClose } from '@/assets/icons'
import { guardarianService } from '@/utils/GuardarianService'

import { GuardarianModalSteps } from './GuardarianModal.types'
import { GuardarianBillingInfo, GuardarianModalBillingInfoStep } from './steps/GuardarianModalBillingInfoStep'
import { GuardarianForm, GuardarianModalFormStep } from './steps/GuardarianModalFormStep'
import { GuardarianProgressModal } from './steps/GuardarianProgressModal'

import { Button } from '../_buttons/Button'
import { DialogModal } from '../_overlays/DialogModal'

type LoadingSetter = (value: boolean) => void
export type SetActionButtonHandler = (setLoading?: LoadingSetter) => void | Promise<void>

type GuardarianData = GuardarianBillingInfo & GuardarianForm

export const GuardarianModal = ({ onClose }: { onClose: () => void }) => {
const [step, setStep] = useState(GuardarianModalSteps.INFO)
const [checkoutUrl, setCheckoutUrl] = useState('')
const [forceCloseFrame, setForceCloseFrame] = useState(false)
const [transactionId, setTransactionId] = useState('')
const [primaryAction, setPrimaryAction] = useState<undefined | SetActionButtonHandler>(undefined)
const formRef = useRef<GuardarianData>({
from: {
currency: undefined,
amount: undefined,
},
to: {
currency: undefined,
amount: undefined,
},
})

return (
<DialogModal
size={step === GuardarianModalSteps.FORM ? 'small' : 'medium'}
title="Guardarian"
show
primaryButton={
[GuardarianModalSteps.FORM, GuardarianModalSteps.INFO].includes(step)
? {
text: 'Continue',
onClick: () => primaryAction?.(),
}
: undefined
}
additionalActionsNode={
<Button variant="secondary" onClick={() => onClose()}>
Cancel
</Button>
}
>
{step === GuardarianModalSteps.INFO ? (
<GuardarianModalBillingInfoStep
onSubmit={(data) => {
formRef.current = {
...formRef.current,
...data,
}
setStep(GuardarianModalSteps.FORM)
}}
setActionButtonHandler={(fn) => setPrimaryAction(() => fn)}
/>
) : null}
{step === GuardarianModalSteps.FORM ? (
<GuardarianModalFormStep
onSubmit={async (data) => {
formRef.current = {
...formRef.current,
...data,
}
const { from, to, ...billingInfo } = formRef.current

const response = await guardarianService.createTransaction({
amount: from.amount ?? 0,
from: from as { currency: string },
to: to as { currency: string },
billingInfo: {
...billingInfo,
dob: billingInfo?.dob?.toISOString() ?? '',
} as {
email: string
country: string
region: string
city: string
street: string
apartment: string
postIndex: string
firstName: string
lastName: string
dob: string
},
})

setCheckoutUrl(response.data.redirect_url)
setTransactionId(String(response.data.id))

setStep(GuardarianModalSteps.PROGRESS)
}}
setActionButtonHandler={(fn) => setPrimaryAction(() => fn)}
/>
) : null}
{step === GuardarianModalSteps.FAILED ? <div>failed</div> : null}
{step === GuardarianModalSteps.SUCCESS ? <div>success</div> : null}
{step === GuardarianModalSteps.TIMEOUT ? <div>transaction timeouted</div> : null}
{step === GuardarianModalSteps.PROGRESS && transactionId ? (
<GuardarianProgressModal redirectUrl={checkoutUrl ?? ''} transactionId={transactionId} goToStep={setStep} />
) : null}
{checkoutUrl && !forceCloseFrame
? ReactDOM.createPortal(
<FrameBox>
<FrameCloseButton onClick={() => setForceCloseFrame(true)} variant="warning" icon={<SvgActionClose />} />
<StyledFrame src={checkoutUrl} />
</FrameBox>,
document.body
)
: null}
</DialogModal>
)
}

const FrameBox = styled.div`
position: absolute;
inset: 0;
display: grid;
place-items: center;
`

export const FrameCloseButton = styled(Button)`
z-index: 111111111111111111111;
position: absolute;
top: 10px;
right: 10px;
`

export const StyledFrame = styled.iframe`
width: 90vw;
height: 90vh;
z-index: 9999;
`
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export enum GuardarianModalSteps {
INFO,
FORM,
PROGRESS,
TIMEOUT,
FAILED,
SUCCESS,
}
1 change: 1 addition & 0 deletions packages/atlas/src/components/GuardarianModal/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './GuardarianModal'
Loading
Loading