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

[AR-8266] wallet update login flow screens #229

Merged
merged 15 commits into from
Aug 8, 2024
255 changes: 165 additions & 90 deletions src/ui/components.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ import {
useRef,
Dispatch,
} from 'preact/hooks'
import { ARCANA_LOGO, getSocialLogo } from './icons'
import { ICONS } from '../utils'
import { ARCANA_LOGO, getSocialLogo, MISC_ICONS } from './icons'
import { ModalParams } from './typings'
import { Theme } from '../typings'
import { JSXInternal } from 'preact/src/jsx'
Expand All @@ -17,7 +16,7 @@ import './style.css'
const Header = ({ compact, logo }: { compact: boolean; logo: string }) => {
const [loaded, setLoaded] = useState(false)
const showLogoContainer = () => {
setLoaded(false)
setLoaded(true)
}
return (
<>
Expand All @@ -35,10 +34,7 @@ const Header = ({ compact, logo }: { compact: boolean; logo: string }) => {
</div>
{!compact ? (
<div className="xar-header-text">
<h1 className="xar-header-heading">Welcome</h1>
<p className="xar-header-subtext">
We’ll email you a login link for a password-free sign in.
</p>
<h1 className="xar-header-heading">Log In</h1>
</div>
) : (
''
Expand All @@ -51,9 +47,11 @@ const EmailLogin = ({
loginWithOTPStart,
email,
setEmail,
mode,
}: {
email: string
setEmail: Dispatch<StateUpdater<string>>
mode: Theme
} & Pick<ModalParams, 'loginWithOTPStart'>) => {
const [disabled, setDisabled] = useState(true)
const onInput: JSXInternal.GenericEventHandler<HTMLInputElement> = (e) => {
Expand All @@ -78,50 +76,112 @@ const EmailLogin = ({
useEffect(() => {
setDisabled(!isEmail(email))
}, [])

return (
<form className="xar-email-login">
<input
value={email}
onInput={onInput}
className="xar-email-login__input"
type="text"
placeholder={'Enter your email'}
/>
<button disabled={disabled} onClick={clickHandler} className="xar-btn">
Get Login OTP
</button>
<div class="xar-email-login__input-container">
<input
value={email}
onInput={onInput}
className="xar-email-login__input"
type="text"
placeholder={'Enter your email'}
/>
<button
disabled={disabled}
onClick={clickHandler}
class="xar-btn xar-btn__input-arrow"
>
<img src={MISC_ICONS[mode].arrow} alt="proceed" />
</button>
</div>
</form>
)
}

const Separator = ({ text }: { text: string }) => {
return <div className="xar-separator">{text}</div>
return (
<div className="xar-separator">
<span class="xar-separator__text">{text}</span>
</div>
)
}

const SocialLogin = ({
loginWithSocial,
loginList,
mode,
}: Pick<ModalParams, 'loginWithSocial' | 'loginList'> & { mode: Theme }) => {
setShowMore,
}: { setShowMore: Dispatch<StateUpdater<boolean>> } & Pick<
ModalParams,
'loginWithSocial' | 'loginList'
> & { mode: Theme }) => {
const clickHandler = (p: string) => {
return loginWithSocial(p)
}
return (
<div className="xar-social-container">
{loginList.map((l) => {
return (
<div
className="xar-social-icon__wrapper"
onClick={() => clickHandler(l)}
>
<img
src={getSocialLogo(l, mode)}
alt={`${l} logo`}
className="xar-social-icon"
/>
</div>
)
})}
{loginList.length <= 4
? loginList.map((l: string) => {
return (
<div
className="xar-social-icon__wrapper-full-width"
onClick={() => clickHandler(l)}
>
<img
src={getSocialLogo(l, mode)}
alt={`${l} logo`}
className="xar-social-icon"
/>
<p>Continue with {l.charAt(0).toUpperCase() + l.slice(1)}</p>
</div>
)
})
: loginList.slice(0, 5).map((l, i) => {
return i === 0 ? (
<div
className="xar-social-icon__wrapper-full-width"
onClick={() => clickHandler(l)}
>
<img
src={getSocialLogo(l, mode)}
alt={`${l} logo`}
className="xar-social-icon"
/>
<p>Continue with {l.charAt(0).toUpperCase() + l.slice(1)}</p>
</div>
) : (
<div
className="xar-social-icon__wrapper"
onClick={() => clickHandler(l)}
style={
loginList.length === 5
? { width: '60px', height: '44px', borderRadius: '40%' }
: {}
}
>
<img
src={getSocialLogo(l, mode)}
alt={`${l} logo`}
className="xar-social-icon"
/>
</div>
)
})}
{loginList.length > 5 ? (
<div
className="xar-social-icon__wrapper"
onClick={() => setShowMore(true)}
>
<img
src={MISC_ICONS[mode]['dots-horizontal']}
alt="more"
className="xar-social-icon"
/>
</div>
) : (
''
)}
</div>
)
}
Expand All @@ -135,7 +195,7 @@ const Footer = ({ mode }: { mode: Theme }) => {
target="_blank"
className="xar-footer-img__link"
>
<img className="xar-footer-img" src={logo} alt="Secured By Arcana" />
<img className="xar-footer-img" src={logo} alt="Powered By Arcana" />
</a>
</div>
)
Expand All @@ -150,15 +210,7 @@ const Loader = (props: {
}) => {
return (
<>
{props.header ? (
props.header
) : (
<ProgressOval
compact={props.compact}
stroke={8}
secondaryColor="#8D8D8D"
/>
)}
{props.header ? props.header : <ProgressOval />}
{props.text ? <p className="xar-loader__text">{props.text}</p> : ''}
{props.children ? <>{props.children}</> : ''}
</>
Expand All @@ -171,6 +223,9 @@ const OTPEntry = ({
setError,
closeFunc,
compact,
email,
mode,
toHome,
}: {
loginWithOtpStart: () => Promise<unknown>
loginWithOtpComplete: (
Expand All @@ -180,6 +235,9 @@ const OTPEntry = ({
setError(): void
closeFunc(): void
compact: boolean
email: string
mode: Theme
toHome(): void
}) => {
const { counter, resetCounter } = useCounter(30)
const [attempts, setAttempts] = useState(3)
Expand Down Expand Up @@ -368,62 +426,78 @@ const OTPEntry = ({
if (loader.loading) {
return (
<>
<ProgressOval stroke={8} secondaryColor="#8D8D8D" compact={compact} />
<ProgressOval />
<div class="xar-loader__text">{loader.text}</div>
</>
)
}

return (
<>
<div class="xar-otp-heading">Verification</div>
<div class="xar-otp-sub-heading">
Please enter the OTP that was sent to your <br />
email address
<div class="xar-otp-heading-container">
<button
class="xar-btn"
style={{ position: 'absolute' }}
onClick={toHome}
>
<img src={MISC_ICONS[mode]['back-arrow']} alt="back" />
</button>
<p class="xar-otp-heading">Enter OTP</p>
</div>
<div className="xar-otp-box">
{Array(numInputs)
.fill(null)
.map((_, i) => {
return (
<input
value={getOTPValue()[i] ?? ''}
key={i}
type="text"
maxLength={1}
autoComplete="off"
ref={(el) => (inputRefs.current[i] = el)}
onFocus={(event) => handleFocus(event)(i)}
onInput={handleInputChange}
onKeyDown={handleKeyDown}
onPaste={handlePaste}
className={
isInvalidOTP
? 'xar-otp-input xar-invalid-otp'
: 'xar-otp-input'
}
/>
)
})}
<div>
<img src={MISC_ICONS[mode].email} alt="email" />
</div>
<div class="xar-otp-sub-heading">
We’ve sent a verification code to
<br />
<span class="xar-otp-email">{email}</span>
</div>
{isInvalidOTP ? (
<div>
<p class="xar-invalid-otp-text">
Incorrect OTP. {attempts} attempts left.
</p>
<div className="xar-otp-box-container">
<div class="xar-otp-box">
{Array(numInputs)
.fill(null)
.map((_, i) => {
return (
<input
value={getOTPValue()[i] ?? ''}
key={i}
type="text"
maxLength={1}
autoComplete="off"
ref={(el) => (inputRefs.current[i] = el)}
onFocus={(event) => handleFocus(event)(i)}
onInput={handleInputChange}
onKeyDown={handleKeyDown}
onPaste={handlePaste}
className={
isInvalidOTP
? 'xar-otp-input xar-invalid-otp'
: 'xar-otp-input'
}
/>
)
})}
</div>
) : (
''
)}
{isInvalidOTP ? (
<div>
<p class="xar-invalid-otp-text">
Incorrect OTP. {attempts} attempts left.
</p>
</div>
) : (
''
)}
</div>

<div>
<Action
disabled={counter > 0}
text={
counter > 0 ? `Resend code in ${counter} seconds` : 'Resend code'
}
method={resendCode}
/>
{counter > 0 ? (
<span class="xar-sub-text">Resend code in {counter} seconds</span>
) : (
<div>
<span class="xar-sub-text">Did not receive your code yet?</span>
<Action text={'Re-send code'} method={resendCode} />
</div>
)}
</div>
</>
)
Expand All @@ -450,16 +524,17 @@ const useCounter = (time = 60) => {
return { counter, resetCounter }
}

const OTPError = ({ action }: { action: () => void }) => {
const OTPError = ({ action, mode }: { action: () => void; mode: Theme }) => {
return (
<>
<img class="xar-header-logo" src={ICONS.fail} alt="failed" />
<img class="xar-header-logo" src={MISC_ICONS[mode].failed} alt="failed" />
<h2 class="xar-otp-error-heading">Login Failed</h2>
<p class="xar-otp-error-subheading">
Please check credentials and try again
</p>
<button className="xar-btn" onClick={action}>
Go Home
<img src={MISC_ICONS[mode]['try-again']} alt="Try again" />
<span class="xar-action__link">Try Again</span>
</button>
</>
)
Expand Down
Loading
Loading