From 6ba996ed63ceae09dd455a15050f4a6d8df57acc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nguy=E1=BB=85n=20Ho=C3=A0i=20Danh?=
<33005392+nguyenhoaidanh@users.noreply.github.com>
Date: Thu, 26 Oct 2023 09:39:22 +0700
Subject: [PATCH 01/26] update: tooltip limit order (#2333)
---
src/components/swapv2/LimitOrder/useValidateInputError.tsx | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/src/components/swapv2/LimitOrder/useValidateInputError.tsx b/src/components/swapv2/LimitOrder/useValidateInputError.tsx
index 1e158c458d..3a158ebb26 100644
--- a/src/components/swapv2/LimitOrder/useValidateInputError.tsx
+++ b/src/components/swapv2/LimitOrder/useValidateInputError.tsx
@@ -53,12 +53,13 @@ const useValidateInputError = ({
return (
- You don't have sufficient {currencyIn?.symbol} balance. After your active orders, you have{' '}
+ Insufficient {currencyIn?.symbol} balance.
+
setInputValue(remainBalance.toExact())}>
{!remainBalance.equalTo(JSBI.BigInt(0)) ? '~' : ''}
{formatNum} {currencyIn?.symbol}
{' '}
- left.
+ remaining after deducting Active and Open orders.
)
From ab0dc67f6f4fabe13d858674d0f3d36be4d2c5e6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nguy=E1=BB=85n=20Ho=C3=A0i=20Danh?=
<33005392+nguyenhoaidanh@users.noreply.github.com>
Date: Thu, 26 Oct 2023 10:16:14 +0700
Subject: [PATCH 02/26] feat: sign in email for kyberAI (#2296)
---
package.json | 2 +-
src/components/Header/web3/SelectWallet.tsx | 2 +-
.../Header/web3/SignWallet/ConfirmModal.tsx | 8 +-
.../Header/web3/SignWallet/ProfileContent.tsx | 17 ++-
src/hooks/useLogin.tsx | 123 ++++++++----------
src/hooks/useSessionExpire.ts | 8 +-
.../NotificationPreference/InputEmail.tsx | 74 ++++++++---
.../NotificationPreference/index.tsx | 34 ++---
.../NotificationCenter/Profile/AvatarEdit.tsx | 2 +-
.../Profile/WarningSignMessage.tsx | 2 +-
.../NotificationCenter/Profile/index.tsx | 48 +++----
src/pages/Oauth/AuthForm/ButtonEth.tsx | 55 +++-----
src/pages/Oauth/AuthForm/ButtonGoogle.tsx | 6 +-
src/pages/Oauth/AuthForm/EmailLoginForm.tsx | 73 +++++++++++
src/pages/Oauth/AuthForm/index.tsx | 72 +++++-----
src/pages/Oauth/AuthForm/useAutoSignIn.tsx | 11 +-
src/pages/Oauth/Login.tsx | 65 ++++-----
src/pages/Oauth/helpers.ts | 37 +++++-
.../pages/RegisterWhitelist/SignInForm.tsx | 58 +++++++++
.../pages/RegisterWhitelist/SubscribeForm.tsx | 4 +-
.../pages/RegisterWhitelist/index.tsx | 26 +---
src/pages/Verify/VerifyCodeModal/index.tsx | 75 +++++++----
src/state/authen/reducer.ts | 2 +-
src/state/profile/hooks.ts | 26 ++--
src/state/user/hooks.tsx | 4 +-
yarn.lock | 8 +-
26 files changed, 510 insertions(+), 332 deletions(-)
create mode 100644 src/pages/Oauth/AuthForm/EmailLoginForm.tsx
create mode 100644 src/pages/TrueSightV2/pages/RegisterWhitelist/SignInForm.tsx
diff --git a/package.json b/package.json
index 285bf6399e..67d7d6bdbe 100644
--- a/package.json
+++ b/package.json
@@ -46,7 +46,7 @@
"@blocto/web3-react-connector": "^1.0.0",
"@coinbase/wallet-sdk": "^3.0.4",
"@esbuild-plugins/node-globals-polyfill": "^0.2.3",
- "@kybernetwork/oauth2": "1.0.0",
+ "@kybernetwork/oauth2": "1.0.1",
"@kyberswap/krystal-walletconnect-v2": "0.0.1",
"@kyberswap/ks-sdk-classic": "^1.0.3",
"@kyberswap/ks-sdk-core": "1.0.13",
diff --git a/src/components/Header/web3/SelectWallet.tsx b/src/components/Header/web3/SelectWallet.tsx
index ca87b9b531..fe6fe28ef6 100644
--- a/src/components/Header/web3/SelectWallet.tsx
+++ b/src/components/Header/web3/SelectWallet.tsx
@@ -166,7 +166,7 @@ function Web3StatusInner() {
style={{ cursor: 'pointer', fontSize: '12px', color: theme.primary }}
onClick={e => {
e.stopPropagation()
- signIn(account)
+ signIn({ account })
}}
>
sign-in
diff --git a/src/components/Header/web3/SignWallet/ConfirmModal.tsx b/src/components/Header/web3/SignWallet/ConfirmModal.tsx
index f14f4cecf7..c1adaffec9 100644
--- a/src/components/Header/web3/SignWallet/ConfirmModal.tsx
+++ b/src/components/Header/web3/SignWallet/ConfirmModal.tsx
@@ -1,3 +1,4 @@
+import { LoginMethod } from '@kybernetwork/oauth2'
import { Trans } from '@lingui/macro'
import { useEffect, useState } from 'react'
import { LogIn, X } from 'react-feather'
@@ -75,7 +76,10 @@ const ModalConfirmProfile: React.FC = () => {
const onCancel = async () => {
const isGuest = !desiredAccountExist
- await signIn(isGuest ? undefined : account, isGuest)
+ await signIn({
+ account: isGuest ? undefined : account,
+ loginMethod: isGuest ? LoginMethod.ANONYMOUS : LoginMethod.ETH,
+ })
hideModal()
}
@@ -209,7 +213,7 @@ const ModalConfirmProfile: React.FC = () => {
{!desiredAccountExist && !connectSuccess && (
signIn(account)}
+ onClick={() => signIn({ account })}
style={{
color: theme.subText,
display: 'flex',
diff --git a/src/components/Header/web3/SignWallet/ProfileContent.tsx b/src/components/Header/web3/SignWallet/ProfileContent.tsx
index 5514030ad2..48c1f1315c 100644
--- a/src/components/Header/web3/SignWallet/ProfileContent.tsx
+++ b/src/components/Header/web3/SignWallet/ProfileContent.tsx
@@ -1,4 +1,4 @@
-import KyberOauth2 from '@kybernetwork/oauth2'
+import KyberOauth2, { LoginMethod } from '@kybernetwork/oauth2'
import { Trans } from '@lingui/macro'
import { rgba } from 'polished'
import { useState } from 'react'
@@ -24,7 +24,7 @@ import { useToggleModal } from 'state/application/hooks'
import { ConnectedProfile, useProfileInfo } from 'state/profile/hooks'
import { MEDIA_WIDTHS } from 'theme'
import getShortenAddress from 'utils/getShortenAddress'
-import { isEmailValid, shortString } from 'utils/string'
+import { shortString } from 'utils/string'
const ContentWrapper = styled.div`
display: flex;
@@ -120,7 +120,7 @@ const ProfileItemWrapper = styled(RowBetween)<{ active: boolean }>`
`
const ProfileItem = ({
- data: { active, guest, address: account, profile, id },
+ data: { active, name: account, profile, id, type },
totalGuest,
}: {
data: ConnectedProfile
@@ -132,11 +132,16 @@ const ProfileItem = ({
const toggleModal = useToggleModal(ApplicationModal.SWITCH_PROFILE_POPUP)
const { signIn, signOut } = useLogin()
const [loading, setLoading] = useState(false)
+ const guest = type === LoginMethod.ANONYMOUS
const onClick = async () => {
if (active || loading) return
setLoading(true)
- await signIn(id, guest, true)
+ await signIn({
+ account: id,
+ loginMethod: type,
+ showSessionExpired: true,
+ })
setLoading(false)
toggleModal()
}
@@ -178,7 +183,7 @@ const ProfileItem = ({
fontSize={active ? '16px' : profile?.nickname ? '12px' : '16px'}
color={active ? theme.subText : theme.subText}
>
- {guest || isEmailValid(account) ? shortString(account, 20) : getShortenAddress(account)}
+ {type === LoginMethod.ETH ? getShortenAddress(account) : shortString(account, 20)}
{active && signOutBtn}
@@ -220,7 +225,7 @@ const ProfileContent = ({ scroll, toggleModal }: { scroll?: boolean; toggleModal
{listNotActive.map(data => (
-
+
))}
diff --git a/src/hooks/useLogin.tsx b/src/hooks/useLogin.tsx
index 2c492a0f31..df7ae49f0e 100644
--- a/src/hooks/useLogin.tsx
+++ b/src/hooks/useLogin.tsx
@@ -14,7 +14,6 @@ import useParsedQueryString from 'hooks/useParsedQueryString'
import { useNotify, useWalletModalToggle } from 'state/application/hooks'
import {
useIsAutoLoginAfterConnectWallet,
- useSessionInfo,
useSetConfirmChangeProfile,
useSetPendingAuthentication,
} from 'state/authen/hooks'
@@ -27,7 +26,6 @@ import {
useSaveUserProfile,
useSignedAccountInfo,
} from 'state/profile/hooks'
-import { filterTruthy, isAddress } from 'utils'
import { setLoginRedirectUrl } from 'utils/redirectUponLogin'
import { isEmailValid } from 'utils/string'
@@ -42,10 +40,9 @@ export const initializeOauthKyberSwap = () => {
initializeOauthKyberSwap()
const useLogin = (autoLogin = false) => {
- const { account, chainId, isEVM } = useActiveWeb3React()
+ const { account } = useActiveWeb3React()
const [createProfile] = useGetOrCreateProfileMutation()
- // const [connectWalletToProfile] = useConnectWalletToProfileMutation()
const notify = useNotify()
const toggleWalletModal = useWalletModalToggle()
const { signedMethod, signedAccount } = useSignedAccountInfo()
@@ -59,28 +56,17 @@ const useLogin = (autoLogin = false) => {
const getProfile = useCallback(
async ({
walletAddress,
- isAnonymous,
- session,
+ loginMethod,
account,
}: {
walletAddress: string | undefined
- isAnonymous: boolean
+ loginMethod: LoginMethod
account: string
- session: any
}) => {
+ const isAnonymous = loginMethod === LoginMethod.ANONYMOUS
try {
const profile = await createProfile().unwrap()
- if (walletAddress && isAddress(chainId, walletAddress)) {
- // await connectWalletToProfile({ walletAddress }) // temp off
- }
-
const formatProfile = { ...profile }
- if (isEmailValid(account) && session) {
- // sign in with google
- formatProfile.avatarUrl = session?.picture ?? ''
- formatProfile.email = session?.email ?? ''
- formatProfile.nickname = filterTruthy([session?.first_name, session?.last_name]).join(' ')
- }
setProfile({ profile: formatProfile, isAnonymous, account })
} catch (error) {
const e = new Error('createProfile Error', { cause: error })
@@ -89,49 +75,49 @@ const useLogin = (autoLogin = false) => {
setProfile({ profile: undefined, isAnonymous, account })
}
},
- [createProfile, setProfile, chainId],
+ [createProfile, setProfile],
)
const showSignInSuccess = useCallback(
- (desireAccount: string | undefined, guest = false) =>
- !autoLogin &&
+ (desireAccount: string | undefined, loginMethod: LoginMethod) => {
+ const isGuest = loginMethod === LoginMethod.ANONYMOUS
+ if (autoLogin) return
notify(
{
type: NotificationType.SUCCESS,
title: t`Signed in successfully`,
summary:
desireAccount?.toLowerCase() === account?.toLowerCase()
- ? t`Connected successfully with the current wallet address`
- : t`Connected successfully with ${
- isEmailValid(desireAccount)
- ? `email ${desireAccount}`
- : guest
- ? `Guest Profile`
- : `profile ${getProfileName(desireAccount, guest)}`
- }`,
+ ? t`Connected successfully with the current wallet address.`
+ : isGuest
+ ? t`Connected successfully with Guest Profile.`
+ : t`Connected successfully with profile ${getProfileName(desireAccount, isGuest)}.`,
},
10_000,
- ),
+ )
+ },
[account, notify, autoLogin, getProfileName],
)
const signInAnonymous = useCallback(
async (guestAccountParam?: string, showSuccessMsg = true) => {
- let userInfo
const guestAccount = guestAccountParam || KEY_GUEST_DEFAULT
let hasError = false
try {
setLoading(true)
- const resp = await KyberOauth2.loginAnonymous(guestAccount === KEY_GUEST_DEFAULT ? undefined : guestAccount)
- userInfo = resp.userInfo
+ await KyberOauth2.loginAnonymous(guestAccount === KEY_GUEST_DEFAULT ? undefined : guestAccount)
saveSignedAccount({ account: guestAccount, method: LoginMethod.ANONYMOUS })
} catch (error) {
console.log('sign in anonymous err', error)
hasError = true
} finally {
setLoading(false)
- await getProfile({ walletAddress: account, isAnonymous: true, account: guestAccount, session: userInfo })
- !hasError && showSuccessMsg && showSignInSuccess(guestAccount, true)
+ await getProfile({
+ walletAddress: account,
+ account: guestAccount,
+ loginMethod: LoginMethod.ANONYMOUS,
+ })
+ !hasError && showSuccessMsg && showSignInSuccess(guestAccount, LoginMethod.ANONYMOUS)
}
},
[getProfile, setLoading, account, saveSignedAccount, showSignInSuccess],
@@ -143,7 +129,7 @@ const useLogin = (autoLogin = false) => {
try {
setLoading(true)
const { loginMethod, userInfo } = await KyberOauth2.getSession(
- isEmailValid(desireAccount ?? '') || !desireAccount
+ isEmailValid(desireAccount) || !desireAccount
? { account: desireAccount }
: { method: LoginMethod.ETH, account: desireAccount },
)
@@ -151,11 +137,10 @@ const useLogin = (autoLogin = false) => {
saveSignedAccount({ account: respAccount, method: loginMethod })
await getProfile({
walletAddress: respAccount,
- isAnonymous: false,
- session: userInfo,
+ loginMethod,
account: respAccount,
})
- showSignInSuccess(respAccount)
+ showSignInSuccess(respAccount, loginMethod)
} catch (error) {
console.log('sdk get session err:', desireAccount, error.message)
if (loginAnonymousIfFailed) {
@@ -168,23 +153,30 @@ const useLogin = (autoLogin = false) => {
[setLoading, signInAnonymous, getProfile, saveSignedAccount, showSignInSuccess],
)
- const redirectSignIn = useCallback(
- (account: string) => {
- if (window.location.pathname.startsWith(APP_PATHS.IAM_LOGIN)) return
- setLoginRedirectUrl(window.location.href)
- KyberOauth2.authenticate(isEVM ? { wallet_address: account } : {}) // navigate to login page
- },
- [isEVM],
- )
+ const redirectSignIn = useCallback((account: string, loginMethod = LoginMethod.ETH) => {
+ if (window.location.pathname.startsWith(APP_PATHS.IAM_LOGIN)) return
+ setLoginRedirectUrl(window.location.href)
+ const accountKey = loginMethod === LoginMethod.ETH ? 'wallet_address' : 'email'
+ KyberOauth2.authenticate({ [accountKey]: account, type: loginMethod }) // navigate to login page
+ }, [])
// check account info and redirect if needed
const [, setAutoSignIn] = useIsAutoLoginAfterConnectWallet()
const signIn = useCallback(
- async (desireAccount?: string, showSessionExpired = false) => {
+ async ({
+ desireAccount,
+ showSessionExpired,
+ loginMethod = LoginMethod.ETH,
+ }: {
+ desireAccount?: string
+ showSessionExpired?: boolean
+ loginMethod?: LoginMethod
+ }) => {
const isAddAccount = !desireAccount
const isSelectAccount = !!desireAccount
+ const isEth = loginMethod === LoginMethod.ETH
- if (isAddAccount && !account) {
+ if (isAddAccount && !account && isEth) {
toggleWalletModal()
setAutoSignIn({ value: true, account: desireAccount })
return
@@ -198,19 +190,19 @@ const useLogin = (autoLogin = false) => {
return
}
- const formatAccount = desireAccount || account || ''
+ const formatAccount = desireAccount || (isEth ? account : '') || ''
if (showSessionExpired && isSelectAccount && !isTokenExist) {
showConfirm({
isOpen: true,
content: t`Your session has expired. Please sign-in to continue.`,
title: t`Session Expired`,
confirmText: t`Sign-in`,
- onConfirm: () => redirectSignIn(formatAccount),
+ onConfirm: () => redirectSignIn(formatAccount, loginMethod),
cancelText: t`Cancel`,
})
return
}
- redirectSignIn(formatAccount)
+ redirectSignIn(formatAccount, loginMethod)
},
[account, checkSessionSignIn, toggleWalletModal, showConfirm, setAutoSignIn, redirectSignIn],
)
@@ -305,8 +297,18 @@ const useLogin = (autoLogin = false) => {
)
const signInWrapped = useCallback(
- (desireAccount: string | undefined = undefined, isGuest = false, showSessionExpired = false) => {
- return isGuest ? signInAnonymous(desireAccount) : signIn(desireAccount, showSessionExpired)
+ ({
+ account,
+ loginMethod = LoginMethod.ETH,
+ showSessionExpired = false,
+ }: {
+ account?: string
+ loginMethod?: LoginMethod
+ showSessionExpired?: boolean
+ } = {}) => {
+ return loginMethod === LoginMethod.ANONYMOUS
+ ? signInAnonymous(account)
+ : signIn({ desireAccount: account, showSessionExpired, loginMethod })
},
[signInAnonymous, signIn],
)
@@ -326,9 +328,7 @@ export const useAutoLogin = () => {
const { signedMethod, signedAccount } = useSignedAccountInfo()
const qs = useParsedQueryString()
const { account } = useActiveWeb3React()
- const { userInfo } = useSessionInfo()
const [isKeepCurrentProfile] = useIsKeepCurrentProfile()
- // const [connectWalletToProfile] = useConnectWalletToProfileMutation()
const { signIn, checkSessionSignIn, signInAnonymous } = useLogin(true)
// auto try sign in when the first visit app, call once
@@ -353,19 +353,10 @@ export const useAutoLogin = () => {
useIsAutoLoginAfterConnectWallet()
useEffect(() => {
if (!account || !needSignInAfterConnectWallet) return
- signIn(accountSignAfterConnectedWallet)
+ signIn({ account: accountSignAfterConnectedWallet })
setAutoSignIn({ value: false, account: undefined })
}, [account, needSignInAfterConnectWallet, accountSignAfterConnectedWallet, signIn, setAutoSignIn])
- // call api connect-wallet to guest profile
- useEffect(() => {
- if (signedMethod === LoginMethod.ANONYMOUS && account && userInfo?.identityId) {
- try {
- // connectWalletToProfile({ walletAddress: account })
- } catch (error) {}
- }
- }, [account, userInfo?.identityId, signedMethod])
-
const setConfirm = useSetConfirmChangeProfile()
// show confirm change profile when change wallet
diff --git a/src/hooks/useSessionExpire.ts b/src/hooks/useSessionExpire.ts
index e000a5aca1..0c466bf4fe 100644
--- a/src/hooks/useSessionExpire.ts
+++ b/src/hooks/useSessionExpire.ts
@@ -8,6 +8,7 @@ import { APP_PATHS } from 'constants/index'
import useLogin from 'hooks/useLogin'
import { ConfirmModalState } from 'state/application/reducer'
import { useSignedAccountInfo } from 'state/profile/hooks'
+import { isEmailValid } from 'utils/string'
export default function useSessionExpiredGlobal() {
const { pathname } = useLocation()
@@ -25,7 +26,10 @@ export default function useSessionExpiredGlobal() {
title: t`Session Expired`,
confirmText: t`Sign-in`,
cancelText: t`Cancel`,
- onConfirm: () => redirectSignIn(accountId || signedAccount),
+ onConfirm: () => {
+ const account = accountId || signedAccount
+ redirectSignIn(account, isEmailValid(account) ? LoginMethod.EMAIL : undefined)
+ },
onCancel: () => {
signInAnonymous(KyberOauth2.getConnectedAnonymousAccounts()[0])
},
@@ -54,7 +58,7 @@ export default function useSessionExpiredGlobal() {
const accountSignHasChanged = signedMethod !== newLoginMethod || signedAccount !== newSignedAccount
if (document.visibilityState === 'visible' && accountSignHasChanged) {
// sync account in multi window tab
- signIn(newSignedAccount, newLoginMethod === LoginMethod.ANONYMOUS)
+ signIn({ account: newSignedAccount, loginMethod: newLoginMethod })
}
} catch (error) {}
}
diff --git a/src/pages/NotificationCenter/NotificationPreference/InputEmail.tsx b/src/pages/NotificationCenter/NotificationPreference/InputEmail.tsx
index 9d8c878332..e296483147 100644
--- a/src/pages/NotificationCenter/NotificationPreference/InputEmail.tsx
+++ b/src/pages/NotificationCenter/NotificationPreference/InputEmail.tsx
@@ -6,6 +6,7 @@ import { ButtonLight } from 'components/Button'
import Input from 'components/Input'
import Tooltip from 'components/Tooltip'
import useTheme from 'hooks/useTheme'
+import VerifyCodeModal from 'pages/Verify/VerifyCodeModal'
const CheckIcon = styled(Check)`
position: absolute;
@@ -26,28 +27,26 @@ const ButtonVerify = styled(ButtonLight)`
const InputWrapper = styled.div`
position: relative;
`
-
-export default function InputEmail({
- errorColor,
+type Props = {
+ onChange: (val: string) => void
+ isVerifiedEmail?: boolean
+ value: string
+ disabled?: boolean
+ hasError?: boolean
+ showVerifyModal?: () => void
+ style?: CSSProperties
+ placement?: string
+}
+export function InputEmail({
value,
onChange,
isVerifiedEmail,
showVerifyModal,
disabled,
hasError,
- color,
style,
-}: {
- errorColor?: string
- onChange: (val: string) => void
- isVerifiedEmail: boolean
- value: string
- disabled?: boolean
- hasError?: boolean
- showVerifyModal: () => void
- color?: string
- style?: CSSProperties
-}) {
+ placement,
+}: Props) {
const theme = useTheme()
return (
onChange(e.target.value)}
/>
{!isVerifiedEmail && value && (
-
+ {
+ e.preventDefault()
+ showVerifyModal?.()
+ }}
+ >
Verify
)}
- {isVerifiedEmail && value && }
+ {isVerifiedEmail && value && !hasError && }
)
}
+
+export default function InputEmailWithVerification(
+ props: Props & {
+ isShowVerify: boolean
+ onDismissVerifyModal: () => void
+ sendCodeFn?: (data: { email: string }) => Promise
+ verifyCodeFn?: (data: { email: string; code: string }) => Promise
+ getErrorMsgFn?: (err: any) => string
+ },
+) {
+ const { value, isShowVerify, onDismissVerifyModal, sendCodeFn, verifyCodeFn, getErrorMsgFn } = props
+ return (
+ <>
+
+
+ >
+ )
+}
diff --git a/src/pages/NotificationCenter/NotificationPreference/index.tsx b/src/pages/NotificationCenter/NotificationPreference/index.tsx
index 153edacb22..a2c977f7f5 100644
--- a/src/pages/NotificationCenter/NotificationPreference/index.tsx
+++ b/src/pages/NotificationCenter/NotificationPreference/index.tsx
@@ -18,12 +18,10 @@ import useMixpanel, { MIXPANEL_TYPE } from 'hooks/useMixpanel'
import useNotification, { Topic, TopicType } from 'hooks/useNotification'
import useTheme from 'hooks/useTheme'
import ActionButtons from 'pages/NotificationCenter/NotificationPreference/ActionButtons'
-import InputEmail from 'pages/NotificationCenter/NotificationPreference/InputEmail'
+import InputEmailWithVerification from 'pages/NotificationCenter/NotificationPreference/InputEmail'
import { PROFILE_MANAGE_ROUTES } from 'pages/NotificationCenter/const'
-import VerifyCodeModal from 'pages/Verify/VerifyCodeModal'
import { useNotify } from 'state/application/hooks'
import { useSessionInfo } from 'state/authen/hooks'
-import { useSignedAccountInfo } from 'state/profile/hooks'
import { useIsWhiteListKyberAI } from 'state/user/hooks'
import { pushUnique } from 'utils'
import { isEmailValid } from 'utils/string'
@@ -110,8 +108,6 @@ export const useValidateEmail = (defaultEmail?: string) => {
const [inputEmail, setInputEmail] = useState(defaultEmail || '')
const [errorInput, setErrorInput] = useState(null)
- const theme = useTheme()
-
const validateInput = useCallback((value: string) => {
const isValid = isEmailValid(value)
const errMsg = t`Please input a valid email address`
@@ -127,9 +123,6 @@ export const useValidateEmail = (defaultEmail?: string) => {
[validateInput],
)
- const hasErrorInput = !!errorInput
- const errorColor = hasErrorInput ? theme.red : theme.border
-
const reset = useCallback(
(email: string | undefined) => {
setErrorInput(null)
@@ -138,7 +131,7 @@ export const useValidateEmail = (defaultEmail?: string) => {
[defaultEmail],
)
- return { inputEmail: inputEmail.trim(), onChangeEmail, errorInput, errorColor, hasErrorInput, reset }
+ return { inputEmail: inputEmail.trim(), onChangeEmail, errorInput, reset }
}
function NotificationPreference({ toggleModal = noop }: { toggleModal?: () => void }) {
@@ -147,9 +140,9 @@ function NotificationPreference({ toggleModal = noop }: { toggleModal?: () => vo
const { account } = useActiveWeb3React()
const { userInfo, isLogin } = useSessionInfo()
- const { isSignInEmail } = useSignedAccountInfo()
const { isWhiteList } = useIsWhiteListKyberAI()
+ const { inputEmail, errorInput, onChangeEmail, reset } = useValidateEmail(userInfo?.email)
const [isShowVerify, setIsShowVerify] = useState(false)
const showVerifyModal = () => {
setIsShowVerify(true)
@@ -165,7 +158,8 @@ function NotificationPreference({ toggleModal = noop }: { toggleModal?: () => vo
const { mixpanelHandler } = useMixpanel()
const [emailPendingVerified, setEmailPendingVerified] = useState('')
- const { inputEmail, errorInput, onChangeEmail, errorColor, reset, hasErrorInput } = useValidateEmail(userInfo?.email)
+
+ const hasErrorInput = !!errorInput
const [selectedTopic, setSelectedTopic] = useState([])
@@ -375,16 +369,20 @@ function NotificationPreference({ toggleModal = noop }: { toggleModal?: () => vo
-
- {errorInput && }
+ {errorInput && (
+
+ )}
@@ -461,12 +459,6 @@ function NotificationPreference({ toggleModal = noop }: { toggleModal?: () => vo
}
/>
)}
-
)
}
diff --git a/src/pages/NotificationCenter/Profile/AvatarEdit.tsx b/src/pages/NotificationCenter/Profile/AvatarEdit.tsx
index afd5009d8f..9e3e15fc22 100644
--- a/src/pages/NotificationCenter/Profile/AvatarEdit.tsx
+++ b/src/pages/NotificationCenter/Profile/AvatarEdit.tsx
@@ -40,7 +40,7 @@ export default function AvatarEdit({
handleFileChange,
size,
}: {
- disabled: boolean
+ disabled?: boolean
size: string
avatar: string | undefined
handleFileChange: (imgUrl: string, file: File) => void
diff --git a/src/pages/NotificationCenter/Profile/WarningSignMessage.tsx b/src/pages/NotificationCenter/Profile/WarningSignMessage.tsx
index 189673d167..972bfc58c4 100644
--- a/src/pages/NotificationCenter/Profile/WarningSignMessage.tsx
+++ b/src/pages/NotificationCenter/Profile/WarningSignMessage.tsx
@@ -63,7 +63,7 @@ const WarningSignMessage = () => {
Read More
)}
- signIn(account)}>
+ signIn({ account })}>
Sign-in
diff --git a/src/pages/NotificationCenter/Profile/index.tsx b/src/pages/NotificationCenter/Profile/index.tsx
index 6c0ca88943..091f00adba 100644
--- a/src/pages/NotificationCenter/Profile/index.tsx
+++ b/src/pages/NotificationCenter/Profile/index.tsx
@@ -21,13 +21,12 @@ import { useUploadImageToCloud } from 'hooks/social'
import useLogin from 'hooks/useLogin'
import useTheme from 'hooks/useTheme'
import { useValidateEmail } from 'pages/NotificationCenter/NotificationPreference'
-import InputEmail from 'pages/NotificationCenter/NotificationPreference/InputEmail'
+import InputEmailWithVerification from 'pages/NotificationCenter/NotificationPreference/InputEmail'
import AvatarEdit from 'pages/NotificationCenter/Profile/AvatarEdit'
import ExportAccountButton from 'pages/NotificationCenter/Profile/ExportAccountButton'
import WarningSignMessage from 'pages/NotificationCenter/Profile/WarningSignMessage'
import { ButtonLogout, ButtonSave } from 'pages/NotificationCenter/Profile/buttons'
import { PROFILE_MANAGE_ROUTES } from 'pages/NotificationCenter/const'
-import VerifyCodeModal from 'pages/Verify/VerifyCodeModal'
import { useNotify } from 'state/application/hooks'
import { useSessionInfo } from 'state/authen/hooks'
import { useIsKeepCurrentProfile, useProfileInfo, useRefreshProfile, useSignedAccountInfo } from 'state/profile/hooks'
@@ -119,11 +118,20 @@ export default function Profile() {
const isMobile = useMedia(`(max-width: ${MEDIA_WIDTHS.upToMedium}px)`)
const { chainId } = useActiveWeb3React()
const { userInfo } = useSessionInfo()
- const { inputEmail, onChangeEmail, errorColor, hasErrorInput } = useValidateEmail(userInfo?.email)
+
+ const { inputEmail, onChangeEmail, errorInput } = useValidateEmail(userInfo?.email)
+ const [isShowVerify, setIsShowVerify] = useState(false)
+ const showVerifyModal = () => {
+ setIsShowVerify(true)
+ }
+ const onDismissVerifyModal = () => {
+ setIsShowVerify(false)
+ }
+
const [nickname, setNickName] = useState('')
const { signOut } = useLogin()
const navigate = useNavigate()
- const { isSignInEmail, isSignInEth, signedAccount, isSigInGuest } = useSignedAccountInfo()
+ const { isSignInEth, signedAccount, isSigInGuest } = useSignedAccountInfo()
const { totalGuest } = useProfileInfo()
const canSignOut = !isSigInGuest || (isSigInGuest && totalGuest > 1)
@@ -158,14 +166,6 @@ export default function Profile() {
file && setFile(file)
}, [userInfo, onChangeEmail, prevIdentity])
- const [isShowVerify, setIsShowVerify] = useState(false)
- const showVerifyModal = () => {
- setIsShowVerify(true)
- }
- const onDismissVerifyModal = () => {
- setIsShowVerify(false)
- }
-
const handleFileChange = (imgUrl: string, file: File) => {
setFile(file)
setPreviewImage(imgUrl)
@@ -276,7 +276,6 @@ export default function Profile() {
value={nickname}
onChange={e => onChangeNickname(e.target.value)}
placeholder="Your nickname"
- disabled={isSignInEmail}
/>
@@ -289,15 +288,15 @@ export default function Profile() {
Email Address (Optional)
-
@@ -306,12 +305,7 @@ export default function Profile() {
-
+
@@ -334,12 +328,6 @@ export default function Profile() {
)}
-
)
}
diff --git a/src/pages/Oauth/AuthForm/ButtonEth.tsx b/src/pages/Oauth/AuthForm/ButtonEth.tsx
index c95509a665..42b33dffc0 100644
--- a/src/pages/Oauth/AuthForm/ButtonEth.tsx
+++ b/src/pages/Oauth/AuthForm/ButtonEth.tsx
@@ -1,31 +1,28 @@
import { LoginMethod } from '@kybernetwork/oauth2'
import { Trans } from '@lingui/macro'
-import { useCallback } from 'react'
-import { Flex, Text } from 'rebass'
+import React, { useCallback } from 'react'
+import { Text } from 'rebass'
-import { ButtonOutlined, ButtonPrimary } from 'components/Button'
+import { ButtonLight, ButtonPrimary } from 'components/Button'
import Wallet from 'components/Icons/Wallet'
import Loader from 'components/Loader'
import { useActiveWeb3React } from 'hooks'
import useAutoSignIn from 'pages/Oauth/AuthForm/useAutoSignIn'
import { FlowStatus } from 'pages/Oauth/Login'
import { useWalletModalToggle } from 'state/application/hooks'
-import { navigateToUrl } from 'utils/redirect'
const ButtonEth = ({
loading,
disabled,
onClick,
flowStatus,
- showBtnCancel,
- backUrl,
+ primary,
}: {
disabled: boolean
loading: boolean
onClick: () => void
- backUrl: string | undefined
flowStatus: FlowStatus
- showBtnCancel: boolean
+ primary: boolean
}) => {
const toggleWalletModal = useWalletModalToggle()
const { account } = useActiveWeb3React()
@@ -40,31 +37,16 @@ const ButtonEth = ({
useAutoSignIn({ onClick: onClickEth, flowStatus, method: LoginMethod.ETH })
- return (
-
- {showBtnCancel && (
- {
- e.preventDefault()
- navigateToUrl(backUrl)
- }}
- >
- Cancel
-
- )}
- {
- e.preventDefault()
- onClickEth()
- }}
- disabled={disabled}
- >
+ const propsEth = {
+ height: '36px',
+ id: 'btnLoginEth',
+ onClick: (e: MouseEvent) => {
+ e.preventDefault()
+ onClickEth()
+ },
+ disabled: disabled || loading,
+ children: (
+ <>
{loading ? (
<>
@@ -80,9 +62,10 @@ const ButtonEth = ({
Sign-In with Wallet
>
)}
-
-
- )
+ >
+ ),
+ }
+ return React.createElement(primary ? ButtonPrimary : ButtonLight, propsEth)
}
export default ButtonEth
diff --git a/src/pages/Oauth/AuthForm/ButtonGoogle.tsx b/src/pages/Oauth/AuthForm/ButtonGoogle.tsx
index 34e1896d94..e80d598fee 100644
--- a/src/pages/Oauth/AuthForm/ButtonGoogle.tsx
+++ b/src/pages/Oauth/AuthForm/ButtonGoogle.tsx
@@ -7,11 +7,11 @@ import useAutoSignIn from 'pages/Oauth/AuthForm/useAutoSignIn'
import { FlowStatus } from 'pages/Oauth/Login'
interface Props {
- outline: boolean
+ primary: boolean
flowStatus: FlowStatus
}
-const ButtonGoogle: React.FC = ({ outline, flowStatus }) => {
+const ButtonGoogle: React.FC = ({ primary, flowStatus }) => {
const ref = useRef(null)
const { autoLoginMethod } = flowStatus
const isAutoLogin = autoLoginMethod === LoginMethod.GOOGLE
@@ -32,6 +32,6 @@ const ButtonGoogle: React.FC = ({ outline, flowStatus }) => {
children: Sign-In with Google,
style: isAutoLogin ? { opacity: 0 } : undefined,
}
- return React.createElement(outline ? ButtonOutlined : ButtonPrimary, props)
+ return React.createElement(primary ? ButtonPrimary : ButtonOutlined, props)
}
export default ButtonGoogle
diff --git a/src/pages/Oauth/AuthForm/EmailLoginForm.tsx b/src/pages/Oauth/AuthForm/EmailLoginForm.tsx
new file mode 100644
index 0000000000..51c78128a1
--- /dev/null
+++ b/src/pages/Oauth/AuthForm/EmailLoginForm.tsx
@@ -0,0 +1,73 @@
+import KyberOauth2, { LoginMethod } from '@kybernetwork/oauth2'
+import { Trans, t } from '@lingui/macro'
+import { useState } from 'react'
+import styled from 'styled-components'
+
+import { ButtonPrimary } from 'components/Button'
+import Column from 'components/Column'
+import useParsedQueryString from 'hooks/useParsedQueryString'
+import { useValidateEmail } from 'pages/NotificationCenter/NotificationPreference'
+import InputEmailWithVerification from 'pages/NotificationCenter/NotificationPreference/InputEmail'
+import useAutoSignIn from 'pages/Oauth/AuthForm/useAutoSignIn'
+import { FlowStatus, getIamErrorMsg } from 'pages/Oauth/Login'
+import { isEmailValid, queryStringToObject } from 'utils/string'
+
+const Wrapper = styled(Column)`
+ width: 100%;
+ justify-content: center;
+ gap: 16px;
+`
+
+const EmailLoginForm = ({ flowStatus }: { flowStatus: FlowStatus }) => {
+ const { email } = useParsedQueryString<{ email: string }>()
+ const { inputEmail, errorInput, onChangeEmail } = useValidateEmail(isEmailValid(email) ? email || '' : '')
+
+ const [isShowVerify, setIsShowVerify] = useState(false)
+ const onDismissVerifyModal = () => {
+ setIsShowVerify(false)
+ }
+
+ const onVerifyEmail = (e?: React.MouseEvent) => {
+ e?.preventDefault?.()
+ if (errorInput || isShowVerify || !inputEmail) return
+ setIsShowVerify(true)
+ }
+
+ useAutoSignIn({ method: LoginMethod.EMAIL, onClick: onVerifyEmail, flowStatus })
+
+ const onVerifyCode = async (data: { code: string; email: string }) => {
+ const resp = await KyberOauth2.oauthUi.loginEmail(data)
+ console.debug('oauth resp login email', resp)
+ }
+
+ const onSendCode = async ({ email }: { email: string }) => {
+ return KyberOauth2.oauthUi.sendVerifyCode({
+ email,
+ flow: queryStringToObject(window.location.search).flow + '',
+ csrf: flowStatus.csrf,
+ })
+ }
+
+ return (
+
+
+
+ Sign-In with Email
+
+
+ )
+}
+
+export default EmailLoginForm
diff --git a/src/pages/Oauth/AuthForm/index.tsx b/src/pages/Oauth/AuthForm/index.tsx
index 0d287d748d..dd1384333f 100644
--- a/src/pages/Oauth/AuthForm/index.tsx
+++ b/src/pages/Oauth/AuthForm/index.tsx
@@ -1,15 +1,13 @@
import { LoginFlow, LoginMethod } from '@kybernetwork/oauth2'
-import React from 'react'
-import { isMobile } from 'react-device-detect'
+import React, { Fragment, useMemo } from 'react'
import { Flex } from 'rebass'
import styled from 'styled-components'
-import useParsedQueryString from 'hooks/useParsedQueryString'
import useTheme from 'hooks/useTheme'
import ButtonEth from 'pages/Oauth/AuthForm/ButtonEth'
import ButtonGoogle from 'pages/Oauth/AuthForm/ButtonGoogle'
+import EmailLoginForm from 'pages/Oauth/AuthForm/EmailLoginForm'
import { FlowStatus } from 'pages/Oauth/Login'
-import { validateRedirectURL } from 'utils/redirect'
import { getSupportLoginMethods } from '../helpers'
import AuthFormFieldMessage from './AuthFormMessage'
@@ -19,6 +17,8 @@ const Form = styled.form`
flex-direction: column;
align-items: center;
gap: 14px;
+ width: 340px;
+ max-width: 90vw;
`
interface AuthFormProps extends React.FormHTMLAttributes {
@@ -30,38 +30,52 @@ interface AuthFormProps extends React.FormHTMLAttributes {
const Splash = () =>
-const AuthForm: React.FC = ({ formConfig, signInWithEth, flowStatus, disableEth }) => {
- const { back_uri } = useParsedQueryString<{ back_uri: string }>()
+export const OrDivider = () => {
const theme = useTheme()
- if (!formConfig) return null
+ return (
+
+ or
+
+ )
+}
- const { autoLoginMethod, processingSignIn } = flowStatus
- const { ui } = formConfig
- const loginMethods = getSupportLoginMethods(formConfig)
+const AuthForm: React.FC = ({ formConfig, signInWithEth, flowStatus, disableEth }) => {
+ const { processingSignIn } = flowStatus
- const showEth = loginMethods.includes(LoginMethod.ETH) && autoLoginMethod !== LoginMethod.GOOGLE
- const hasGoogle = loginMethods.includes(LoginMethod.GOOGLE)
- const showBtnCancel = !isMobile && !hasGoogle && validateRedirectURL(back_uri) && !processingSignIn
- const hasBothEthAndGoogle = hasGoogle && showEth
- return (
-
)
}
diff --git a/src/pages/Oauth/AuthForm/useAutoSignIn.tsx b/src/pages/Oauth/AuthForm/useAutoSignIn.tsx
index 25c825ab2e..31b59e2514 100644
--- a/src/pages/Oauth/AuthForm/useAutoSignIn.tsx
+++ b/src/pages/Oauth/AuthForm/useAutoSignIn.tsx
@@ -1,6 +1,7 @@
import { LoginMethod } from '@kybernetwork/oauth2'
import { useEffect, useRef } from 'react'
+import { useActiveWeb3React } from 'hooks'
import { useEagerConnect } from 'hooks/web3/useEagerConnect'
import { FlowStatus } from 'pages/Oauth/Login'
@@ -14,14 +15,18 @@ const useAutoSignIn = ({
flowStatus: FlowStatus
}) => {
const autoSelect = useRef(false)
+ const { account } = useActiveWeb3React()
const { current: triedEager } = useEagerConnect()
useEffect(() => {
if (autoSelect.current || !flowReady || autoLoginMethod !== method) return
- if ((triedEager && autoLoginMethod === LoginMethod.ETH) || autoLoginMethod === LoginMethod.GOOGLE) {
+ if (
+ (triedEager && autoLoginMethod === LoginMethod.ETH && account) ||
+ [LoginMethod.GOOGLE, LoginMethod.EMAIL].includes(autoLoginMethod)
+ ) {
autoSelect.current = true
- onClick()
+ onClick?.()
}
- }, [flowReady, autoLoginMethod, onClick, triedEager, method])
+ }, [flowReady, autoLoginMethod, onClick, triedEager, method, account])
}
export default useAutoSignIn
diff --git a/src/pages/Oauth/Login.tsx b/src/pages/Oauth/Login.tsx
index 4b0a288308..5e9809b978 100644
--- a/src/pages/Oauth/Login.tsx
+++ b/src/pages/Oauth/Login.tsx
@@ -1,5 +1,5 @@
import KyberOauth2, { LoginFlow, LoginMethod } from '@kybernetwork/oauth2'
-import { Trans } from '@lingui/macro'
+import { t } from '@lingui/macro'
import { useCallback, useEffect, useRef, useState } from 'react'
import Loader from 'components/Loader'
@@ -14,28 +14,28 @@ import { queryStringToObject } from 'utils/string'
import { formatSignature } from 'utils/transaction'
import AuthForm from './AuthForm'
-import { createSignMessage, getSupportLoginMethods } from './helpers'
+import { canAutoSignInEth, createSignMessage, extractAutoLoginMethod, getSupportLoginMethods } from './helpers'
-const getErrorMsg = (error: any) => {
+export const getIamErrorMsg = (error: any) => {
const data = error?.response?.data
const isExpired = data?.error?.id === 'self_service_flow_expired'
- if (isExpired) {
- return (
-
- Time to sign-in is Expired, please go back and try again.
-
- )
- }
+ if (isExpired) return t`Time to sign-in is Expired, please go back and try again.`
- return data?.ui?.messages?.[0]?.text || data?.error?.reason || data?.error?.message || error?.message || error + ''
+ const message = data?.ui?.messages?.[0]
+ if (message?.id === 4000001) return t`Verification code is wrong or expired. Please try again.`
+ return message?.text || data?.error?.reason || data?.error?.message || error?.message || error + ''
}
export type FlowStatus = {
processingSignIn: boolean
flowReady: boolean
+ csrf: string
autoLoginMethod: LoginMethod | undefined // not waiting for click btn
}
+const getCsrfToken = (loginFlow: LoginFlow | undefined) =>
+ loginFlow?.ui?.nodes?.find(e => e.attributes.name === 'csrf_token')?.attributes?.value ?? ''
+
export function Login() {
const { account, chainId } = useActiveWeb3React()
const { library: provider } = useWeb3React()
@@ -46,18 +46,15 @@ export function Login() {
flowReady: false,
autoLoginMethod: undefined,
processingSignIn: false,
+ csrf: '',
})
const { wallet_address } = useParsedQueryString<{ wallet_address: string }>()
const loginMethods = getSupportLoginMethods(authFormConfig)
- const isSignInEth = loginMethods.includes(LoginMethod.ETH)
+ const showMsgSignInEth = account && canAutoSignInEth(loginMethods)
const isMismatchEthAddress =
- !loginMethods.includes(LoginMethod.GOOGLE) &&
- isSignInEth &&
- wallet_address &&
- account &&
- wallet_address?.toLowerCase() !== account?.toLowerCase()
+ showMsgSignInEth && wallet_address && wallet_address?.toLowerCase() !== account?.toLowerCase()
const connectingWallet = useRef(false)
@@ -68,9 +65,8 @@ export function Login() {
return
}
setFlowStatus(v => ({ ...v, processingSignIn: true }))
- const { ui, challenge, issued_at } = authFormConfig
+ const { challenge, issued_at } = authFormConfig
connectingWallet.current = true
- const csrf = ui.nodes.find(e => e.attributes.name === 'csrf_token')?.attributes?.value ?? ''
const message = createSignMessage({
address: account,
chainId,
@@ -83,7 +79,7 @@ export function Login() {
const resp = await KyberOauth2.oauthUi.loginEthereum({
address: account,
signature: formatSignature(signature),
- csrf,
+ csrf: getCsrfToken(authFormConfig),
chainId,
})
@@ -93,7 +89,7 @@ export function Login() {
}
} catch (error: any) {
if (!didUserReject(error)) {
- setError(getErrorMsg(error))
+ setError(getIamErrorMsg(error))
}
console.error('signInWithEthereum err', error)
connectingWallet.current = false
@@ -110,26 +106,17 @@ export function Login() {
setAuthFormConfig(loginFlow)
const { client_id } = loginFlow.oauth_client
- const loginMethods = getSupportLoginMethods(loginFlow)
-
- let autoLoginMethod: LoginMethod | undefined
- const isIncludeGoogle = loginMethods.includes(LoginMethod.GOOGLE)
- if (loginMethods.length === 1) {
- if (loginMethods.includes(LoginMethod.ANONYMOUS)) {
- throw new Error('Not found login method for this app')
- }
- if (isIncludeGoogle) {
- autoLoginMethod = LoginMethod.GOOGLE
- }
- }
- if (loginMethods.includes(LoginMethod.ETH) && !isIncludeGoogle) {
- autoLoginMethod = LoginMethod.ETH
- }
KyberOauth2.initialize({ clientId: client_id, mode: ENV_KEY })
- setFlowStatus(v => ({ ...v, flowReady: true, autoLoginMethod }))
+
+ setFlowStatus(v => ({
+ ...v,
+ flowReady: true,
+ autoLoginMethod: extractAutoLoginMethod(loginFlow),
+ csrf: getCsrfToken(loginFlow),
+ }))
} catch (error: any) {
const { error_description } = queryStringToObject(window.location.search)
- setError(error_description || getErrorMsg(error))
+ setError(error_description || getIamErrorMsg(error))
}
}
getFlowLogin()
@@ -166,7 +153,7 @@ export function Login() {
Checking data ...
- ) : isSignInEth && account ? (
+ ) : showMsgSignInEth ? (
renderEthMsg()
) : (
appName && Please sign in to continue with {appName}
diff --git a/src/pages/Oauth/helpers.ts b/src/pages/Oauth/helpers.ts
index 125dffee13..a84e4c8076 100644
--- a/src/pages/Oauth/helpers.ts
+++ b/src/pages/Oauth/helpers.ts
@@ -1,9 +1,44 @@
-import { LoginFlow } from '@kybernetwork/oauth2'
+import { LoginFlow, LoginMethod } from '@kybernetwork/oauth2'
+
+import { isInEnum, queryStringToObject } from 'utils/string'
export const getSupportLoginMethods = (loginFlow: LoginFlow | undefined) => {
return loginFlow?.oauth_client?.metadata?.allowed_login_methods ?? []
}
+export const canAutoSignInEth = (loginMethods: LoginMethod[]) => {
+ const isIncludeEth = loginMethods.includes(LoginMethod.ETH)
+ const totalMethod = loginMethods.length
+ return (
+ (isIncludeEth && totalMethod === 1) ||
+ (isIncludeEth && totalMethod === 2 && loginMethods.includes(LoginMethod.ANONYMOUS))
+ )
+}
+
+export const extractAutoLoginMethod = (loginFlow: LoginFlow) => {
+ const loginMethods = getSupportLoginMethods(loginFlow)
+ let autoLoginMethod: LoginMethod | undefined
+
+ if (loginMethods.length === 1) {
+ if (loginMethods.includes(LoginMethod.ANONYMOUS)) {
+ throw new Error('Not found login method for this app')
+ }
+ if (loginMethods.includes(LoginMethod.GOOGLE)) {
+ autoLoginMethod = LoginMethod.GOOGLE
+ }
+ }
+ if (canAutoSignInEth(loginMethods)) {
+ autoLoginMethod = LoginMethod.ETH
+ }
+
+ // auto login method from url
+ const { type } = queryStringToObject(window.location.search)
+ if (!autoLoginMethod && isInEnum(type + '', LoginMethod) && loginMethods.includes(type)) {
+ autoLoginMethod = type as LoginMethod
+ }
+ return autoLoginMethod
+}
+
type MessageParams = {
domain: string
uri: string
diff --git a/src/pages/TrueSightV2/pages/RegisterWhitelist/SignInForm.tsx b/src/pages/TrueSightV2/pages/RegisterWhitelist/SignInForm.tsx
new file mode 100644
index 0000000000..ec5fd06aff
--- /dev/null
+++ b/src/pages/TrueSightV2/pages/RegisterWhitelist/SignInForm.tsx
@@ -0,0 +1,58 @@
+import { LoginMethod } from '@kybernetwork/oauth2'
+import { Trans } from '@lingui/macro'
+import { Text } from 'rebass'
+import styled from 'styled-components'
+
+import { ButtonLight, ButtonPrimary } from 'components/Button'
+import Column from 'components/Column'
+import DownloadWalletModal from 'components/DownloadWalletModal'
+import useLogin from 'hooks/useLogin'
+import useTheme from 'hooks/useTheme'
+import { useValidateEmail } from 'pages/NotificationCenter/NotificationPreference'
+import { InputEmail } from 'pages/NotificationCenter/NotificationPreference/InputEmail'
+import { OrDivider } from 'pages/Oauth/AuthForm'
+import { ApplicationModal } from 'state/application/actions'
+import { useOpenModal } from 'state/application/hooks'
+
+const Wrapper = styled(Column)`
+ width: 340px;
+ gap: 16px;
+ align-items: center;
+`
+
+export default function SignInForm() {
+ const { signIn } = useLogin()
+ const theme = useTheme()
+ const openDownloadWalletModal = useOpenModal(ApplicationModal.DOWNLOAD_WALLET)
+ const { inputEmail, errorInput, onChangeEmail } = useValidateEmail('')
+ return (
+
+
+ inputEmail && !errorInput && signIn({ loginMethod: LoginMethod.EMAIL, account: inputEmail })}
+ height={'36px'}
+ >
+ Sign-In
+
+
+ signIn()} height={'36px'}>
+ Sign-In with Wallet
+
+
+
+ Don't have a wallet?{' '}
+
+ Get started here
+
+
+
+
+
+ )
+}
diff --git a/src/pages/TrueSightV2/pages/RegisterWhitelist/SubscribeForm.tsx b/src/pages/TrueSightV2/pages/RegisterWhitelist/SubscribeForm.tsx
index fcacfc53b5..999ed7f715 100644
--- a/src/pages/TrueSightV2/pages/RegisterWhitelist/SubscribeForm.tsx
+++ b/src/pages/TrueSightV2/pages/RegisterWhitelist/SubscribeForm.tsx
@@ -21,13 +21,13 @@ export default function EmailForm({
}: {
showVerify: (email: string, code: string, showSuccess: boolean) => void
}) {
+ const { userInfo } = useSessionInfo()
const { mixpanelHandler } = useMixpanel()
- const [inputEmail, setInputEmail] = useState('')
+ const [inputEmail, setInputEmail] = useState(userInfo?.email || '')
const qs = useParsedQueryString<{ referrer: string }>()
const [referredByCode, setCode] = useState(qs.referrer || '')
const [errorInput, setErrorInput] = useState({ email: '', referredByCode: '' })
- const { userInfo } = useSessionInfo()
const [requestWaitList] = useRequestWhiteListMutation()
const [checkReferalCode] = useLazyCheckReferralCodeQuery()
diff --git a/src/pages/TrueSightV2/pages/RegisterWhitelist/index.tsx b/src/pages/TrueSightV2/pages/RegisterWhitelist/index.tsx
index 28d4245171..3f3942e91d 100644
--- a/src/pages/TrueSightV2/pages/RegisterWhitelist/index.tsx
+++ b/src/pages/TrueSightV2/pages/RegisterWhitelist/index.tsx
@@ -6,21 +6,15 @@ import { useRequestWhiteListMutation } from 'services/kyberAISubscription'
import styled from 'styled-components'
import { ButtonLight, ButtonPrimary } from 'components/Button'
-import Column from 'components/Column'
-import DownloadWalletModal from 'components/DownloadWalletModal'
-import Row from 'components/Row'
import { APP_PATHS } from 'constants/index'
-import useLogin from 'hooks/useLogin'
import { MIXPANEL_TYPE, useMixpanelKyberAI } from 'hooks/useMixpanel'
import useTheme from 'hooks/useTheme'
+import SignInForm from 'pages/TrueSightV2/pages/RegisterWhitelist/SignInForm'
import SubscribeForm from 'pages/TrueSightV2/pages/RegisterWhitelist/SubscribeForm'
import WaitListForm from 'pages/TrueSightV2/pages/RegisterWhitelist/WaitListForm'
import VerifyCodeModal from 'pages/Verify/VerifyCodeModal'
-import { ApplicationModal } from 'state/application/actions'
-import { useOpenModal } from 'state/application/hooks'
import { useSessionInfo } from 'state/authen/hooks'
import { useIsWhiteListKyberAI } from 'state/user/hooks'
-import { ButtonText } from 'theme'
const ConnectWalletButton = styled(ButtonLight)`
height: 36px;
@@ -32,7 +26,6 @@ export default function RegisterWhitelist({ showForm = true }: { showForm?: bool
const theme = useTheme()
const mixpanelHandler = useMixpanelKyberAI()
const { isLogin } = useSessionInfo()
- const { signIn } = useLogin()
const { isWhiteList, isWaitList, loading: isCheckingPermission } = useIsWhiteListKyberAI()
@@ -42,7 +35,6 @@ export default function RegisterWhitelist({ showForm = true }: { showForm?: bool
referredByCode: '',
showVerifySuccess: false,
})
- const openDownloadWalletModal = useOpenModal(ApplicationModal.DOWNLOAD_WALLET)
const showVerify = (email: string, referredByCode: string, showVerifySuccess: boolean) => {
setVerifyModalState({ isOpen: true, referredByCode, email, showVerifySuccess })
@@ -96,21 +88,7 @@ export default function RegisterWhitelist({ showForm = true }: { showForm?: bool
)
- if (!isLogin)
- return (
-
- signIn()}>
- Sign-In to Continue
-
-
- Don't have a wallet?
-
- Get started here
-
-
-
-
- )
+ if (!isLogin) return
const btnGetStart = (
Promise) | (() => void)
isOpen: boolean
@@ -84,6 +89,10 @@ export default function VerifyCodeModal({
showVerifySuccess?: boolean
verifySuccessTitle?: string
verifySuccessContent?: ReactNode
+ sendCodeFn?: (data: { email: string }) => Promise
+ verifyCodeFn?: (data: { email: string; code: string }) => Promise
+ getErrorMsgFn?: (err: any) => string
+ refreshProfile?: boolean
}) {
const theme = useTheme()
const [otp, setOtp] = useState('')
@@ -95,10 +104,12 @@ export default function VerifyCodeModal({
const [isTypingIos, setIsTypingIos] = useState(false)
const isTypingAndroid = useMedia(`(max-height: 450px)`)
- const [expiredDuration, setExpireDuration] = useState(defaultTime)
+ const [expiredDuration, setExpireDuration] = useState(0)
+
const isSendMailError = error === ErrorType.SEND_EMAIL_ERROR
const isVerifyMailError = error === ErrorType.VALIDATE_ERROR
const isRateLimitError = error === ErrorType.RATE_LIMIT
+
const canShowResend =
!isSendMailError && !isRateLimitError && expiredDuration < (timeExpire - 1) * TIMES_IN_SECS.ONE_MIN
@@ -123,20 +134,19 @@ export default function VerifyCodeModal({
[notify],
)
- const sendEmail = useCallback(() => {
+ const sendEmail = useCallback(async () => {
interval.current && clearInterval(interval.current)
if (!email) return
- sendOtp({ email })
- .unwrap()
- .then(() => {
- setExpireDuration(defaultTime)
- setError(undefined)
- })
- .catch(data => {
- setExpireDuration(0)
- setError(!data?.status ? ErrorType.RATE_LIMIT : ErrorType.SEND_EMAIL_ERROR)
- })
- }, [email, sendOtp])
+ try {
+ const promise = sendCodeFn ? sendCodeFn({ email }) : sendOtp({ email }).unwrap()
+ await promise
+ setExpireDuration(defaultTime)
+ setError(undefined)
+ } catch (error) {
+ setExpireDuration(0)
+ setError(!error?.status ? ErrorType.RATE_LIMIT : ErrorType.SEND_EMAIL_ERROR)
+ }
+ }, [email, sendOtp, sendCodeFn])
const checkedRegisterStatus = useRef(false) // prevent spam
const sendEmailWhenInit = useCallback(() => {
@@ -158,26 +168,33 @@ export default function VerifyCodeModal({
}
}, [isOpen, showNotiSuccess, showVerifySuccess, sendEmailWhenInit])
- const refreshProfile = useRefreshProfile()
+ const refreshProfileInfo = useRefreshProfile()
+ const [verifying, setIsVerifying] = useState(false)
const verify = async () => {
+ if (!email || verifying) return
try {
- if (!email) return
- await verifyOtp({ code: otp, email }).unwrap()
- await onVerifySuccess?.()
- await refreshProfile()
- showNotiSuccess()
+ setIsVerifying(true)
+ const promise = verifyCodeFn ? verifyCodeFn({ code: otp, email }) : verifyOtp({ code: otp, email }).unwrap()
+ await promise
+ await (onVerifySuccess ? onVerifySuccess() : onDismiss())
+ if (refreshProfile) {
+ await refreshProfileInfo()
+ showNotiSuccess()
+ }
} catch (error) {
setError(ErrorType.VALIDATE_ERROR)
notify({
title: t`Error`,
- summary: getErrorMessage(error),
+ summary: getErrorMsgFn?.(error) || getErrorMessage(error),
type: NotificationType.ERROR,
})
+ } finally {
+ setIsVerifying(false)
}
}
- const onChange = (value: string) => {
+ const onChangeOTP = (value: string) => {
isVerifyMailError && setError(undefined)
setOtp(value)
}
@@ -244,7 +261,7 @@ export default function VerifyCodeModal({
(
Resend
) : (
-
- Verify
+
+ {verifying ? (
+
+ Verifying
+
+ ) : (
+ Verify
+ )}
)}
diff --git a/src/state/authen/reducer.ts b/src/state/authen/reducer.ts
index 74640b4899..78cc7a32d8 100644
--- a/src/state/authen/reducer.ts
+++ b/src/state/authen/reducer.ts
@@ -6,7 +6,7 @@ export type UserProfile = {
telegramUsername: string
nickname: string
avatarUrl: string
- data: { hasAccessToKyberAI: boolean; favouriteChainIds?: string[] }
+ data?: { hasAccessToKyberAI: boolean; favouriteChainIds?: string[] }
}
export type ConfirmProfile = {
showModal: boolean
diff --git a/src/state/profile/hooks.ts b/src/state/profile/hooks.ts
index 6afdb1c52d..3912058357 100644
--- a/src/state/profile/hooks.ts
+++ b/src/state/profile/hooks.ts
@@ -11,6 +11,7 @@ import { UserProfile, authenActions } from 'state/authen/reducer'
import { useAppDispatch } from 'state/hooks'
import { CacheProfile, ProfileMap, SignedAccountParams, profileActions } from 'state/profile/reducer'
import getShortenAddress from 'utils/getShortenAddress'
+import { isEmailValid } from 'utils/string'
const { setImportToken, setKeepCurrentProfile, setProfileMap, updateSignedAccount } = profileActions
@@ -68,10 +69,10 @@ export function useSaveConnectedProfile() {
export type ConnectedProfile = {
active: boolean
- address: string
- profile: UserProfile | undefined
- guest: boolean
+ name: string
id: string
+ profile: UserProfile | undefined
+ type: LoginMethod
default?: boolean
}
@@ -130,20 +131,20 @@ export const useProfileInfo = (): {
const profile = userInfo || getCacheProfile(signedAccount ? signedAccount : KEY_GUEST_DEFAULT, isSigInGuest)
const profiles = useMemo(() => {
- const getAccountGuest = (account: string) => ({
- address: account === KEY_GUEST_DEFAULT ? t`Guest` : t`Imported Guest`,
+ const getAccountGuest = (account: string): ConnectedProfile => ({
+ name: account === KEY_GUEST_DEFAULT ? t`Guest` : t`Imported Guest`,
active: account === signedAccount?.toLowerCase(),
id: account,
profile: getCacheProfile(account, true),
- guest: true,
+ type: LoginMethod.ANONYMOUS,
default: account === KEY_GUEST_DEFAULT,
})
- const getAccountSignIn = (account: string) => ({
+ const getAccountSignIn = (account: string): ConnectedProfile => ({
active: account === signedAccount?.toLowerCase(),
- address: account,
+ name: account,
id: account,
profile: getCacheProfile(account, false),
- guest: false,
+ type: isEmailValid(account) ? LoginMethod.EMAIL : LoginMethod.ETH,
})
const results = Object.keys(profileInfo?.wallet ?? {})
@@ -170,7 +171,7 @@ export const useProfileInfo = (): {
[saveCacheProfile],
)
- const totalGuest = profiles.reduce((total, cur) => total + (cur.guest ? 1 : 0), 0)
+ const totalGuest = profiles.reduce((total, cur) => total + (cur.type === LoginMethod.ANONYMOUS ? 1 : 0), 0)
return { profiles, totalGuest, profile, removeProfile, removeAllProfile, getCacheProfile, saveCacheProfile }
}
@@ -178,12 +179,12 @@ export const useProfileInfo = (): {
export const useSignedAccountInfo = () => {
const { isEVM } = useActiveWeb3React()
const signedAccount = useSelector((state: AppState) => state.profile.signedAccount)
- const signedMethod = useSelector((state: AppState) => state.profile.signedMethod)
+ const signedMethod = useSelector((state: AppState) => state.profile.signedMethod) as LoginMethod
const { account } = useActiveWeb3React()
const isSigInGuest = signedMethod === LoginMethod.ANONYMOUS
- const isSignInEmail = signedMethod === LoginMethod.GOOGLE
+ const isSignInEmail = signedMethod === LoginMethod.EMAIL || signedMethod === LoginMethod.GOOGLE
const isSignInEth = signedMethod === LoginMethod.ETH
const isSignInDifferentWallet =
@@ -196,7 +197,6 @@ export const useSignedAccountInfo = () => {
isSignInDifferentWallet,
isSigInGuest,
isSignInGuestDefault,
- isSignInEmail,
isSignInEth,
}
}
diff --git a/src/state/user/hooks.tsx b/src/state/user/hooks.tsx
index d9d80fbb9a..456cc28251 100644
--- a/src/state/user/hooks.tsx
+++ b/src/state/user/hooks.tsx
@@ -502,13 +502,13 @@ export const useIsWhiteListKyberAI = () => {
userInfo && getParticipantInfoQuery()
}, [getParticipantInfoQuery, userInfo])
- const { account } = useActiveWeb3React()
const [connectingWallet] = useIsConnectingWallet()
const isLoading = isFetching || pendingAuthentication
const loadingDebounced = useDebounce(isLoading, 500) || connectingWallet
- const participantInfo = isError || loadingDebounced || !account ? participantDefault : rawData
+ const participantInfo = isError || loadingDebounced ? participantDefault : rawData
+
return {
loading: loadingDebounced,
isWhiteList:
diff --git a/yarn.lock b/yarn.lock
index 35100ef88a..8f867837fd 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2257,10 +2257,10 @@
bs58 "^5.0.0"
uuid "^8.3.2"
-"@kybernetwork/oauth2@1.0.0":
- version "1.0.0"
- resolved "https://npm.pkg.github.com/download/@kybernetwork/oauth2/1.0.0/dbb42e967c8a6755bb801b2c528064764534d00e#dbb42e967c8a6755bb801b2c528064764534d00e"
- integrity sha512-fIv7W+s+PIK8xxm+aic4GjtrKZsv8TRuqNfa6CeDGOW16oxhZlSARHVJy4rWfGxAVT2+0GRuXXR2uMSwAdfczw==
+"@kybernetwork/oauth2@1.0.1":
+ version "1.0.1"
+ resolved "https://npm.pkg.github.com/download/@kybernetwork/oauth2/1.0.1/f78ca8ea7fd20290d54aeb191fc60d41ce73bed4#f78ca8ea7fd20290d54aeb191fc60d41ce73bed4"
+ integrity sha512-OiuFv7L5oHJVtsupRYBd06Ss9e/01RAA/PQPnKyDn9rcDmjN52OucdKMALh/5HBcEMyVnNkTeoddubEaLTIJhA==
dependencies:
axios "1.2.1"
client-oauth2 "^4.3.3"
From e700d7b0b3ca78e67b724e0398c0716fc6346757 Mon Sep 17 00:00:00 2001
From: Nguyen Van Viet
Date: Thu, 26 Oct 2023 10:35:41 +0700
Subject: [PATCH 03/26] feat: add red2 (#2295)
---
index.html | 408 +--
public/libs/red2.js | 6267 +++++++++++++++++++++++++++++++++++++++++++
2 files changed, 6491 insertions(+), 184 deletions(-)
create mode 100644 public/libs/red2.js
diff --git a/index.html b/index.html
index 747986cc54..375040fd08 100644
--- a/index.html
+++ b/index.html
@@ -1,62 +1,55 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- KyberSwap - Trading Smart
-
-
-
-
+
+
+
-
-
-
-
-
-
-
-
-
-
-