Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
F
frontend
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
vicotor
frontend
Commits
f363957a
Commit
f363957a
authored
Oct 14, 2024
by
tom
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix linking email and wallet to existing account
parent
000bf0d6
Changes
10
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
43 additions
and
25 deletions
+43
-25
resources.ts
lib/api/resources.ts
+6
-3
AuthModal.tsx
ui/snippets/auth/AuthModal.tsx
+6
-8
AuthModalScreenConnectWallet.tsx
ui/snippets/auth/screens/AuthModalScreenConnectWallet.tsx
+4
-3
AuthModalScreenEmail.tsx
ui/snippets/auth/screens/AuthModalScreenEmail.tsx
+2
-1
AuthModalScreenOtpCode.tsx
ui/snippets/auth/screens/AuthModalScreenOtpCode.tsx
+8
-3
types.ts
ui/snippets/auth/types.ts
+2
-2
useSignInWithWallet.ts
ui/snippets/auth/useSignInWithWallet.ts
+12
-5
UserProfileContentWallet.tsx
ui/snippets/user/profile/UserProfileContentWallet.tsx
+1
-0
UserProfileDesktop.tsx
ui/snippets/user/profile/UserProfileDesktop.tsx
+1
-0
UserProfileMobile.tsx
ui/snippets/user/profile/UserProfileMobile.tsx
+1
-0
No files found.
lib/api/resources.ts
View file @
f363957a
...
@@ -226,18 +226,21 @@ export const RESOURCES = {
...
@@ -226,18 +226,21 @@ export const RESOURCES = {
auth_send_otp
:
{
auth_send_otp
:
{
path
:
'
/api/account/v2/send_otp
'
,
path
:
'
/api/account/v2/send_otp
'
,
},
},
auth_confirm_otp
:
{
auth_confirm_otp
:
{
path
:
'
/api/account/v2/confirm_otp
'
,
path
:
'
/api/account/v2/confirm_otp
'
,
},
},
auth_siwe_message
:
{
auth_siwe_message
:
{
path
:
'
/api/account/v2/siwe_message
'
,
path
:
'
/api/account/v2/siwe_message
'
,
},
},
auth_siwe_verify
:
{
auth_siwe_verify
:
{
path
:
'
/api/account/v2/authenticate_via_wallet
'
,
path
:
'
/api/account/v2/authenticate_via_wallet
'
,
},
},
auth_link_email
:
{
path
:
'
/api/account/v2/email/link
'
,
},
auth_link_address
:
{
path
:
'
/api/account/v2/address/link
'
,
},
// STATS MICROSERVICE API
// STATS MICROSERVICE API
stats_counters
:
{
stats_counters
:
{
...
...
ui/snippets/auth/AuthModal.tsx
View file @
f363957a
import
{
Modal
,
ModalBody
,
ModalCloseButton
,
ModalContent
,
ModalHeader
,
ModalOverlay
}
from
'
@chakra-ui/react
'
;
import
{
Modal
,
ModalBody
,
ModalCloseButton
,
ModalContent
,
ModalHeader
,
ModalOverlay
}
from
'
@chakra-ui/react
'
;
import
{
useQueryClient
}
from
'
@tanstack/react-query
'
;
import
{
useRouter
}
from
'
next/router
'
;
import
{
useRouter
}
from
'
next/router
'
;
import
React
from
'
react
'
;
import
React
from
'
react
'
;
import
{
GoogleReCaptchaProvider
}
from
'
react-google-recaptcha-v3
'
;
import
{
GoogleReCaptchaProvider
}
from
'
react-google-recaptcha-v3
'
;
...
@@ -6,6 +7,7 @@ import { GoogleReCaptchaProvider } from 'react-google-recaptcha-v3';
...
@@ -6,6 +7,7 @@ import { GoogleReCaptchaProvider } from 'react-google-recaptcha-v3';
import
type
{
Screen
,
ScreenSuccess
}
from
'
./types
'
;
import
type
{
Screen
,
ScreenSuccess
}
from
'
./types
'
;
import
config
from
'
configs/app
'
;
import
config
from
'
configs/app
'
;
import
{
getResourceKey
}
from
'
lib/api/useApiQuery
'
;
import
useGetCsrfToken
from
'
lib/hooks/useGetCsrfToken
'
;
import
useGetCsrfToken
from
'
lib/hooks/useGetCsrfToken
'
;
import
*
as
mixpanel
from
'
lib/mixpanel
'
;
import
*
as
mixpanel
from
'
lib/mixpanel
'
;
import
IconSvg
from
'
ui/shared/IconSvg
'
;
import
IconSvg
from
'
ui/shared/IconSvg
'
;
...
@@ -16,7 +18,6 @@ import AuthModalScreenOtpCode from './screens/AuthModalScreenOtpCode';
...
@@ -16,7 +18,6 @@ import AuthModalScreenOtpCode from './screens/AuthModalScreenOtpCode';
import
AuthModalScreenSelectMethod
from
'
./screens/AuthModalScreenSelectMethod
'
;
import
AuthModalScreenSelectMethod
from
'
./screens/AuthModalScreenSelectMethod
'
;
import
AuthModalScreenSuccessEmail
from
'
./screens/AuthModalScreenSuccessEmail
'
;
import
AuthModalScreenSuccessEmail
from
'
./screens/AuthModalScreenSuccessEmail
'
;
import
AuthModalScreenSuccessWallet
from
'
./screens/AuthModalScreenSuccessWallet
'
;
import
AuthModalScreenSuccessWallet
from
'
./screens/AuthModalScreenSuccessWallet
'
;
import
useProfileQuery
from
'
./useProfileQuery
'
;
const
feature
=
config
.
features
.
account
;
const
feature
=
config
.
features
.
account
;
...
@@ -38,8 +39,8 @@ const AuthModal = ({ initialScreen, onClose, mixpanelConfig }: Props) => {
...
@@ -38,8 +39,8 @@ const AuthModal = ({ initialScreen, onClose, mixpanelConfig }: Props) => {
const
[
isSuccess
,
setIsSuccess
]
=
React
.
useState
(
false
);
const
[
isSuccess
,
setIsSuccess
]
=
React
.
useState
(
false
);
const
router
=
useRouter
();
const
router
=
useRouter
();
const
profileQuery
=
useProfileQuery
();
const
csrfQuery
=
useGetCsrfToken
();
const
csrfQuery
=
useGetCsrfToken
();
const
queryClient
=
useQueryClient
();
React
.
useEffect
(()
=>
{
React
.
useEffect
(()
=>
{
if
(
'
isAuth
'
in
initialScreen
&&
initialScreen
.
isAuth
)
{
if
(
'
isAuth
'
in
initialScreen
&&
initialScreen
.
isAuth
)
{
...
@@ -84,13 +85,10 @@ const AuthModal = ({ initialScreen, onClose, mixpanelConfig }: Props) => {
...
@@ -84,13 +85,10 @@ const AuthModal = ({ initialScreen, onClose, mixpanelConfig }: Props) => {
});
});
}
}
const
{
data
}
=
await
profileQuery
.
refetch
(
);
queryClient
.
setQueryData
(
getResourceKey
(
'
user_info
'
),
()
=>
screen
.
profile
);
await
csrfQuery
.
refetch
();
await
csrfQuery
.
refetch
();
if
(
data
)
{
onNextStep
(
screen
);
onNextStep
({
...
screen
,
profile
:
data
});
},
[
initialScreen
,
mixpanelConfig
?.
account_link_info
.
source
,
onNextStep
,
csrfQuery
,
queryClient
]);
}
// TODO @tom2drum handle error case
},
[
initialScreen
,
mixpanelConfig
?.
account_link_info
.
source
,
onNextStep
,
profileQuery
,
csrfQuery
]);
const
onModalClose
=
React
.
useCallback
(()
=>
{
const
onModalClose
=
React
.
useCallback
(()
=>
{
onClose
(
isSuccess
);
onClose
(
isSuccess
);
...
...
ui/snippets/auth/screens/AuthModalScreenConnectWallet.tsx
View file @
f363957a
...
@@ -2,6 +2,7 @@ import { Center, Spinner } from '@chakra-ui/react';
...
@@ -2,6 +2,7 @@ import { Center, Spinner } from '@chakra-ui/react';
import
React
from
'
react
'
;
import
React
from
'
react
'
;
import
type
{
ScreenSuccess
}
from
'
../types
'
;
import
type
{
ScreenSuccess
}
from
'
../types
'
;
import
type
{
UserInfo
}
from
'
types/api/account
'
;
import
type
*
as
mixpanel
from
'
lib/mixpanel
'
;
import
type
*
as
mixpanel
from
'
lib/mixpanel
'
;
...
@@ -17,15 +18,15 @@ interface Props {
...
@@ -17,15 +18,15 @@ interface Props {
const
AuthModalScreenConnectWallet
=
({
onSuccess
,
onError
,
isAuth
,
source
}:
Props
)
=>
{
const
AuthModalScreenConnectWallet
=
({
onSuccess
,
onError
,
isAuth
,
source
}:
Props
)
=>
{
const
isStartedRef
=
React
.
useRef
(
false
);
const
isStartedRef
=
React
.
useRef
(
false
);
const
handleSignInSuccess
=
React
.
useCallback
(({
address
}:
{
address
:
string
})
=>
{
const
handleSignInSuccess
=
React
.
useCallback
(({
address
,
profile
}:
{
address
:
string
;
profile
:
UserInfo
})
=>
{
onSuccess
({
type
:
'
success_wallet
'
,
address
,
isAuth
});
onSuccess
({
type
:
'
success_wallet
'
,
address
,
isAuth
,
profile
});
},
[
onSuccess
,
isAuth
]);
},
[
onSuccess
,
isAuth
]);
const
handleSignInError
=
React
.
useCallback
(()
=>
{
const
handleSignInError
=
React
.
useCallback
(()
=>
{
onError
(
isAuth
);
onError
(
isAuth
);
},
[
onError
,
isAuth
]);
},
[
onError
,
isAuth
]);
const
{
start
}
=
useSignInWithWallet
({
onSuccess
:
handleSignInSuccess
,
onError
:
handleSignInError
,
source
});
const
{
start
}
=
useSignInWithWallet
({
onSuccess
:
handleSignInSuccess
,
onError
:
handleSignInError
,
source
,
isAuth
});
React
.
useEffect
(()
=>
{
React
.
useEffect
(()
=>
{
if
(
!
isStartedRef
.
current
)
{
if
(
!
isStartedRef
.
current
)
{
...
...
ui/snippets/auth/screens/AuthModalScreenEmail.tsx
View file @
f363957a
...
@@ -8,6 +8,7 @@ import type { EmailFormFields, Screen } from '../types';
...
@@ -8,6 +8,7 @@ import type { EmailFormFields, Screen } from '../types';
import
useApiFetch
from
'
lib/api/useApiFetch
'
;
import
useApiFetch
from
'
lib/api/useApiFetch
'
;
import
getErrorMessage
from
'
lib/errors/getErrorMessage
'
;
import
getErrorMessage
from
'
lib/errors/getErrorMessage
'
;
import
getErrorObjPayload
from
'
lib/errors/getErrorObjPayload
'
;
import
useToast
from
'
lib/hooks/useToast
'
;
import
useToast
from
'
lib/hooks/useToast
'
;
import
*
as
mixpanel
from
'
lib/mixpanel
'
;
import
*
as
mixpanel
from
'
lib/mixpanel
'
;
...
@@ -65,7 +66,7 @@ const AuthModalScreenEmail = ({ onSubmit, isAuth, mixpanelConfig }: Props) => {
...
@@ -65,7 +66,7 @@ const AuthModalScreenEmail = ({ onSubmit, isAuth, mixpanelConfig }: Props) => {
toast
({
toast
({
status
:
'
error
'
,
status
:
'
error
'
,
title
:
'
Error
'
,
title
:
'
Error
'
,
description
:
getErrorMessage
(
error
)
||
'
Something went wrong
'
,
description
:
getError
ObjPayload
<
{
message
:
string
}
>
(
error
)?.
message
||
getError
Message
(
error
)
||
'
Something went wrong
'
,
});
});
}
}
},
[
executeRecaptcha
,
apiFetch
,
isAuth
,
onSubmit
,
mixpanelConfig
?.
account_link_info
.
source
,
toast
]);
},
[
executeRecaptcha
,
apiFetch
,
isAuth
,
onSubmit
,
mixpanelConfig
?.
account_link_info
.
source
,
toast
]);
...
...
ui/snippets/auth/screens/AuthModalScreenOtpCode.tsx
View file @
f363957a
...
@@ -5,6 +5,7 @@ import type { SubmitHandler } from 'react-hook-form';
...
@@ -5,6 +5,7 @@ import type { SubmitHandler } from 'react-hook-form';
import
{
FormProvider
,
useForm
}
from
'
react-hook-form
'
;
import
{
FormProvider
,
useForm
}
from
'
react-hook-form
'
;
import
type
{
OtpCodeFormFields
,
ScreenSuccess
}
from
'
../types
'
;
import
type
{
OtpCodeFormFields
,
ScreenSuccess
}
from
'
../types
'
;
import
type
{
UserInfo
}
from
'
types/api/account
'
;
import
useApiFetch
from
'
lib/api/useApiFetch
'
;
import
useApiFetch
from
'
lib/api/useApiFetch
'
;
import
getErrorMessage
from
'
lib/errors/getErrorMessage
'
;
import
getErrorMessage
from
'
lib/errors/getErrorMessage
'
;
...
@@ -35,7 +36,8 @@ const AuthModalScreenOtpCode = ({ email, onSuccess, isAuth }: Props) => {
...
@@ -35,7 +36,8 @@ const AuthModalScreenOtpCode = ({ email, onSuccess, isAuth }: Props) => {
});
});
const
onFormSubmit
:
SubmitHandler
<
OtpCodeFormFields
>
=
React
.
useCallback
((
formData
)
=>
{
const
onFormSubmit
:
SubmitHandler
<
OtpCodeFormFields
>
=
React
.
useCallback
((
formData
)
=>
{
return
apiFetch
(
'
auth_confirm_otp
'
,
{
const
resource
=
isAuth
?
'
auth_link_email
'
:
'
auth_confirm_otp
'
;
return
apiFetch
<
typeof
resource
,
UserInfo
,
unknown
>
(
resource
,
{
fetchParams
:
{
fetchParams
:
{
method
:
'
POST
'
,
method
:
'
POST
'
,
body
:
{
body
:
{
...
@@ -44,8 +46,11 @@ const AuthModalScreenOtpCode = ({ email, onSuccess, isAuth }: Props) => {
...
@@ -44,8 +46,11 @@ const AuthModalScreenOtpCode = ({ email, onSuccess, isAuth }: Props) => {
},
},
},
},
})
})
.
then
(()
=>
{
.
then
((
response
)
=>
{
onSuccess
({
type
:
'
success_email
'
,
email
,
isAuth
});
if
(
!
(
'
name
'
in
response
))
{
throw
Error
(
'
Something went wrong
'
);
}
onSuccess
({
type
:
'
success_email
'
,
email
,
isAuth
,
profile
:
response
});
})
})
.
catch
((
error
)
=>
{
.
catch
((
error
)
=>
{
const
apiError
=
getErrorObjPayload
<
{
message
:
string
}
>
(
error
);
const
apiError
=
getErrorObjPayload
<
{
message
:
string
}
>
(
error
);
...
...
ui/snippets/auth/types.ts
View file @
f363957a
...
@@ -3,12 +3,12 @@ import type { UserInfo } from 'types/api/account';
...
@@ -3,12 +3,12 @@ import type { UserInfo } from 'types/api/account';
export
type
ScreenSuccess
=
{
export
type
ScreenSuccess
=
{
type
:
'
success_email
'
;
type
:
'
success_email
'
;
email
:
string
;
email
:
string
;
profile
?
:
UserInfo
;
profile
:
UserInfo
;
isAuth
?:
boolean
;
isAuth
?:
boolean
;
}
|
{
}
|
{
type
:
'
success_wallet
'
;
type
:
'
success_wallet
'
;
address
:
string
;
address
:
string
;
profile
?
:
UserInfo
;
profile
:
UserInfo
;
isAuth
?:
boolean
;
isAuth
?:
boolean
;
}
}
export
type
Screen
=
{
export
type
Screen
=
{
...
...
ui/snippets/auth/useSignInWithWallet.ts
View file @
f363957a
...
@@ -2,6 +2,8 @@ import { useWeb3Modal } from '@web3modal/wagmi/react';
...
@@ -2,6 +2,8 @@ import { useWeb3Modal } from '@web3modal/wagmi/react';
import
React
from
'
react
'
;
import
React
from
'
react
'
;
import
{
useSignMessage
}
from
'
wagmi
'
;
import
{
useSignMessage
}
from
'
wagmi
'
;
import
type
{
UserInfo
}
from
'
types/api/account
'
;
import
config
from
'
configs/app
'
;
import
config
from
'
configs/app
'
;
import
useApiFetch
from
'
lib/api/useApiFetch
'
;
import
useApiFetch
from
'
lib/api/useApiFetch
'
;
import
getErrorMessage
from
'
lib/errors/getErrorMessage
'
;
import
getErrorMessage
from
'
lib/errors/getErrorMessage
'
;
...
@@ -11,12 +13,13 @@ import * as mixpanel from 'lib/mixpanel';
...
@@ -11,12 +13,13 @@ import * as mixpanel from 'lib/mixpanel';
import
useAccount
from
'
lib/web3/useAccount
'
;
import
useAccount
from
'
lib/web3/useAccount
'
;
interface
Props
{
interface
Props
{
onSuccess
?:
({
address
}:
{
address
:
string
})
=>
void
;
onSuccess
?:
({
address
,
profile
}:
{
address
:
string
;
profile
:
UserInfo
})
=>
void
;
onError
?:
()
=>
void
;
onError
?:
()
=>
void
;
source
?:
mixpanel
.
EventPayload
<
mixpanel
.
EventTypes
.
WALLET_CONNECT
>
[
'
Source
'
];
source
?:
mixpanel
.
EventPayload
<
mixpanel
.
EventTypes
.
WALLET_CONNECT
>
[
'
Source
'
];
isAuth
?:
boolean
;
}
}
function
useSignInWithWallet
({
onSuccess
,
onError
,
source
=
'
Login
'
}:
Props
)
{
function
useSignInWithWallet
({
onSuccess
,
onError
,
source
=
'
Login
'
,
isAuth
}:
Props
)
{
const
[
isPending
,
setIsPending
]
=
React
.
useState
(
false
);
const
[
isPending
,
setIsPending
]
=
React
.
useState
(
false
);
const
isConnectingWalletRef
=
React
.
useRef
(
false
);
const
isConnectingWalletRef
=
React
.
useRef
(
false
);
...
@@ -30,13 +33,17 @@ function useSignInWithWallet({ onSuccess, onError, source = 'Login' }: Props) {
...
@@ -30,13 +33,17 @@ function useSignInWithWallet({ onSuccess, onError, source = 'Login' }: Props) {
try
{
try
{
const
siweMessage
=
await
apiFetch
(
'
auth_siwe_message
'
,
{
queryParams
:
{
address
}
})
as
{
siwe_message
:
string
};
const
siweMessage
=
await
apiFetch
(
'
auth_siwe_message
'
,
{
queryParams
:
{
address
}
})
as
{
siwe_message
:
string
};
const
signature
=
await
signMessageAsync
({
message
:
siweMessage
.
siwe_message
});
const
signature
=
await
signMessageAsync
({
message
:
siweMessage
.
siwe_message
});
await
apiFetch
(
'
auth_siwe_verify
'
,
{
const
resource
=
isAuth
?
'
auth_link_address
'
:
'
auth_siwe_verify
'
;
const
response
=
await
apiFetch
<
typeof
resource
,
UserInfo
,
unknown
>
(
resource
,
{
fetchParams
:
{
fetchParams
:
{
method
:
'
POST
'
,
method
:
'
POST
'
,
body
:
{
message
:
siweMessage
.
siwe_message
,
signature
},
body
:
{
message
:
siweMessage
.
siwe_message
,
signature
},
},
},
});
});
onSuccess
?.({
address
});
if
(
!
(
'
name
'
in
response
))
{
throw
Error
(
'
Something went wrong
'
);
}
onSuccess
?.({
address
,
profile
:
response
});
}
catch
(
error
)
{
}
catch
(
error
)
{
const
errorObj
=
getErrorObj
(
error
);
const
errorObj
=
getErrorObj
(
error
);
const
shortMessage
=
errorObj
&&
'
shortMessage
'
in
errorObj
&&
typeof
errorObj
.
shortMessage
===
'
string
'
?
errorObj
.
shortMessage
:
undefined
;
const
shortMessage
=
errorObj
&&
'
shortMessage
'
in
errorObj
&&
typeof
errorObj
.
shortMessage
===
'
string
'
?
errorObj
.
shortMessage
:
undefined
;
...
@@ -49,7 +56,7 @@ function useSignInWithWallet({ onSuccess, onError, source = 'Login' }: Props) {
...
@@ -49,7 +56,7 @@ function useSignInWithWallet({ onSuccess, onError, source = 'Login' }: Props) {
}
finally
{
}
finally
{
setIsPending
(
false
);
setIsPending
(
false
);
}
}
},
[
apiFetch
,
onError
,
onSuccess
,
signMessageAsync
,
toast
]);
},
[
apiFetch
,
isAuth
,
onError
,
onSuccess
,
signMessageAsync
,
toast
]);
const
start
=
React
.
useCallback
(()
=>
{
const
start
=
React
.
useCallback
(()
=>
{
setIsPending
(
true
);
setIsPending
(
true
);
...
...
ui/snippets/user/profile/UserProfileContentWallet.tsx
View file @
f363957a
...
@@ -52,6 +52,7 @@ const UserProfileContentWallet = ({ onClose, className }: Props) => {
...
@@ -52,6 +52,7 @@ const UserProfileContentWallet = ({ onClose, className }: Props) => {
onClick=
{
handleOpenWalletClick
}
onClick=
{
handleOpenWalletClick
}
isLoading=
{
web3Wallet
.
isOpen
}
isLoading=
{
web3Wallet
.
isOpen
}
flexShrink=
{
0
}
flexShrink=
{
0
}
ml=
"auto"
/>
/>
</
Flex
>
</
Flex
>
);
);
...
...
ui/snippets/user/profile/UserProfileDesktop.tsx
View file @
f363957a
...
@@ -40,6 +40,7 @@ const UserProfileDesktop = ({ buttonSize, buttonVariant = 'header' }: Props) =>
...
@@ -40,6 +40,7 @@ const UserProfileDesktop = ({ buttonSize, buttonVariant = 'header' }: Props) =>
return
;
return
;
}
}
// TODO @tom2drum use auth modal instead
if
(
router
.
pathname
===
'
/apps/[id]
'
)
{
if
(
router
.
pathname
===
'
/apps/[id]
'
)
{
signInWithWallet
.
start
();
signInWithWallet
.
start
();
return
;
return
;
...
...
ui/snippets/user/profile/UserProfileMobile.tsx
View file @
f363957a
...
@@ -34,6 +34,7 @@ const UserProfileMobile = () => {
...
@@ -34,6 +34,7 @@ const UserProfileMobile = () => {
return
;
return
;
}
}
// TODO @tom2drum use auth modal instead
if
(
router
.
pathname
===
'
/apps/[id]
'
)
{
if
(
router
.
pathname
===
'
/apps/[id]
'
)
{
signInWithWallet
.
start
();
signInWithWallet
.
start
();
return
;
return
;
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment