Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
3a18ffc
commit 0731466
Showing
15 changed files
with
587 additions
and
28 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,34 +1,56 @@ | ||
import React, { useCallback, useEffect, useMemo, useState } from 'react'; | ||
import React, { useCallback, useMemo, useState } from 'react'; | ||
export interface SubflowPage { | ||
next: () => void; | ||
back: () => void; | ||
setIndex: React.Dispatch<React.SetStateAction<number>>; | ||
setIndex: React.Dispatch<React.SetStateAction<number | string>>; | ||
context: any; | ||
} | ||
|
||
interface InterfaceSubflowProps { | ||
pages: React.FunctionComponent<SubflowPage>[]; | ||
situationalPages: { [key: string]: React.FunctionComponent<SubflowPage> }; | ||
context: any; | ||
} | ||
|
||
export const InterfaceSubflow = ({ pages }: InterfaceSubflowProps) => { | ||
const [index, setIndex] = useState(0); | ||
const Renderer = ({ Render, params }) => { | ||
return <Render {...params} />; | ||
}; | ||
|
||
const length = useMemo(() => pages.length, [pages]); | ||
export const InterfaceSubflow = ({ | ||
pages, | ||
context, | ||
situationalPages, | ||
}: InterfaceSubflowProps) => { | ||
const [index, setIndex] = useState<number | string>(0); | ||
|
||
useEffect(() => { | ||
setIndex(0); | ||
}, [pages, setIndex]); | ||
const length = useMemo(() => pages.length, [pages]); | ||
|
||
const next = useCallback(() => { | ||
if (typeof index === 'string') return setIndex(0); | ||
if (index < length - 1) { | ||
setIndex(index + 1); | ||
} | ||
}, [index, setIndex, length]); | ||
|
||
const back = useCallback(() => { | ||
if (typeof index === 'string') return setIndex(0); | ||
if (index > 0) { | ||
setIndex(index - 1); | ||
} | ||
}, [index, setIndex]); | ||
|
||
return pages[index]({ next, back, setIndex }); | ||
if (situationalPages && typeof index === 'string') { | ||
return ( | ||
<Renderer | ||
Render={situationalPages[index]} | ||
params={{ next, back, context, setIndex }} | ||
/> | ||
); | ||
} | ||
return ( | ||
<Renderer | ||
Render={pages[index]} | ||
params={{ next, back, context, setIndex }} | ||
/> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
24 changes: 17 additions & 7 deletions
24
src/components/masa-interface/pages/masa-green/airdrop.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,15 +1,25 @@ | ||
import React from 'react'; | ||
import { SubflowPage } from '../../interface-subflow'; | ||
|
||
export const AirdropPage: React.FunctionComponent<SubflowPage> = ({ | ||
next, | ||
back, | ||
}) => { | ||
export const AirdropPage: React.FunctionComponent<SubflowPage> = ({ next }) => { | ||
return ( | ||
<div> | ||
Hello! airdrop page | ||
<button onClick={back}>Go back</button> | ||
<button onClick={next}>Go next</button> | ||
<h2>1,000,000 $Masa Token Airdrop</h2> | ||
<div> | ||
<p> | ||
Earn 10 $MASA tokens for each new user you successfully refer to Masa | ||
</p> | ||
<p> | ||
Each friend you refer must mint a Masa Green SBT to be considered a | ||
successful referral | ||
</p> | ||
</div> | ||
|
||
<div className=""> | ||
<button className={'masa-button'} onClick={() => next()}> | ||
Get verified to start referrals | ||
</button> | ||
</div> | ||
</div> | ||
); | ||
}; |
185 changes: 185 additions & 0 deletions
185
src/components/masa-interface/pages/masa-green/code.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,185 @@ | ||
import ReactCodeInput from '@acusti/react-code-input'; | ||
import { useLocalStorage, useMasa } from '../../../../provider'; | ||
import { SubflowPage } from 'components/masa-interface/interface-subflow'; | ||
import React, { | ||
useCallback, | ||
useEffect, | ||
useMemo, | ||
useRef, | ||
useState, | ||
} from 'react'; | ||
|
||
const errorMsgs = { | ||
expired: 'The code is expired. Please click Resend and try again!', | ||
maxAttempts: 'Max attempts reached. Please try again in 10 minutes', | ||
invalid: 'This code is not valid, please click Resend below and try again', | ||
unexpedted: `We're sorry, an unexpected error has occured`, | ||
}; | ||
|
||
const getRetryTimeout = (attemptNumber?: number): number => { | ||
let result = 120; | ||
|
||
const retryTimeouts: { [key: number]: number } = { | ||
1: 15, | ||
2: 30, | ||
3: 40, | ||
4: 60, | ||
5: 90, | ||
}; | ||
|
||
if (attemptNumber) { | ||
result = retryTimeouts[attemptNumber]; | ||
} | ||
|
||
return result; | ||
}; | ||
|
||
export const CodeInterface: React.FunctionComponent<SubflowPage> = ({ | ||
next, | ||
back, | ||
context, | ||
}) => { | ||
const phoneNumber = useMemo(() => context.phoneNumber, [context]); | ||
const codeRef = useRef(null); | ||
const [code, setCode] = useState<string>(''); | ||
const [showCountDown, setShowCountDown] = useState<boolean>(false); | ||
const [time, setTime] = useState<number>(0); | ||
const { masa, handleGenerateGreen } = useMasa(); | ||
|
||
const isValid = true; | ||
|
||
const [errorMsg, setErrorMsg] = useState<string | null>(null); | ||
|
||
const { localStorageSet } = useLocalStorage(); | ||
|
||
useEffect(() => { | ||
time > 0 && setTimeout(() => setTime(time - 1), 1000); | ||
}, [time]); | ||
|
||
useEffect(() => { | ||
if (phoneNumber.length < 1) { | ||
back(); | ||
} | ||
}, [phoneNumber, back]); | ||
|
||
const resendCode = useCallback(async () => { | ||
setCode(''); | ||
setShowCountDown(true); | ||
const result = await handleGenerateGreen?.(phoneNumber); | ||
|
||
if (result) { | ||
const attempts = result.data?.length; | ||
|
||
if (attempts === 0) { | ||
back(); | ||
} else { | ||
setTime(getRetryTimeout(attempts)); | ||
} | ||
} | ||
}, [phoneNumber, handleGenerateGreen, back]); | ||
|
||
const verify = useCallback( | ||
async (completedCode: string, phone: string) => { | ||
if (masa) { | ||
const verifyResponse = await masa.green.verify(phone, completedCode); | ||
|
||
if (verifyResponse?.status === 'approved') { | ||
localStorageSet(`${phone}`, JSON.stringify(verifyResponse)); | ||
next(); | ||
} else { | ||
switch (verifyResponse?.status) { | ||
case 'pending': | ||
setErrorMsg(errorMsgs.invalid); | ||
break; | ||
case '404': | ||
setErrorMsg(errorMsgs.expired); | ||
break; | ||
case '429': | ||
setErrorMsg(errorMsgs.maxAttempts); | ||
break; | ||
default: | ||
setErrorMsg(errorMsgs.unexpedted); | ||
} | ||
} | ||
} | ||
}, | ||
[masa, next] | ||
); | ||
|
||
const inputStyle = { | ||
borderRadius: '4px', | ||
border: '1px solid', | ||
paddingLeft: '22px', | ||
margin: '6px', | ||
width: '60px', | ||
height: '60px', | ||
fontSize: '22px', | ||
color: 'black', | ||
backgroundColor: 'white', | ||
borderColor: 'lightgrey', | ||
}; | ||
|
||
const invalidStyle = { | ||
borderRadius: '4px', | ||
border: '1px solid', | ||
paddingLeft: '22px', | ||
margin: '6px', | ||
width: '60px', | ||
height: '60px', | ||
fontSize: '22px', | ||
color: 'black', | ||
backgroundColor: 'white', | ||
borderColor: 'red', | ||
}; | ||
|
||
const handleUpdateCode = useCallback( | ||
async (code: string) => { | ||
setCode(code); | ||
|
||
if (code.length === 6) { | ||
await verify(code, phoneNumber); | ||
} | ||
}, | ||
[setCode, phoneNumber, verify] | ||
); | ||
|
||
return ( | ||
<> | ||
<h2>Enter 2FA 6-digit code</h2> | ||
<h3>{`We sent a code to your phone number ending in ${phoneNumber.slice( | ||
-4 | ||
)}.`}</h3> | ||
|
||
<div className="w-full flex flex-col items-center"> | ||
<ReactCodeInput | ||
ref={codeRef} | ||
value={code} | ||
onChange={handleUpdateCode} | ||
inputStyle={inputStyle} | ||
inputStyleInvalid={invalidStyle} | ||
name="green" | ||
type="number" | ||
inputMode="numeric" | ||
fields={6} | ||
isValid={isValid} | ||
/> | ||
<p className={'text-errorRed text-sm mt-8'}>{errorMsg}</p> | ||
<div className={'absolute -bottom-20'}> | ||
<p className={'text-black font-ezra text-lg'}> | ||
Didn't get the code?{' '} | ||
{showCountDown && time !== 0 ? ( | ||
<span className={'font-bold ml-2'}>{time}</span> | ||
) : ( | ||
<span | ||
className={'font-bold cursor-pointer ml-2'} | ||
onClick={() => resendCode()} | ||
> | ||
Resend | ||
</span> | ||
)} | ||
</p> | ||
</div> | ||
</div> | ||
</> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { SubflowPage } from 'components/masa-interface/interface-subflow'; | ||
import React from 'react'; | ||
export const Error: React.FunctionComponent<SubflowPage> = ({ setIndex }) => { | ||
const handleReturn = () => { | ||
setIndex(0); | ||
}; | ||
|
||
return ( | ||
<div> | ||
Error | ||
<button className="masa=button" onClick={handleReturn}> | ||
Return | ||
</button> | ||
</div> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,39 @@ | ||
import React from 'react'; | ||
import React, { useState } from 'react'; | ||
import { InterfaceSubflow } from '../../interface-subflow'; | ||
import { AirdropPage } from './airdrop'; | ||
import { CodeInterface } from './code'; | ||
import { Error } from './error'; | ||
import { NotBotPage } from './not-a-bot'; | ||
import { PhoneInputInterface } from './phone-input'; | ||
import { Success } from './success'; | ||
import { VerifyAndMintInterface } from './verifiy-and-mint'; | ||
|
||
const InterfaceMasaGreen = () => { | ||
const pages = [AirdropPage, NotBotPage]; | ||
const pages = [ | ||
AirdropPage, | ||
NotBotPage, | ||
PhoneInputInterface, | ||
CodeInterface, | ||
VerifyAndMintInterface, | ||
Success, | ||
]; | ||
const [phoneNumber, setPhoneNumber] = useState<string | undefined>(); | ||
|
||
return <InterfaceSubflow pages={pages} />; | ||
const context = { | ||
phoneNumber, | ||
setPhoneNumber, | ||
}; | ||
|
||
return ( | ||
<InterfaceSubflow | ||
pages={pages} | ||
context={context} | ||
situationalPages={{ | ||
success: Success, | ||
error: Error, | ||
}} | ||
/> | ||
); | ||
}; | ||
|
||
export default InterfaceMasaGreen; |
Oops, something went wrong.