@zerodev/wallet-react-ui 0.0.5 → 0.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -10
- package/dist/_types/auth/authStoreSlice.d.ts +3 -4
- package/dist/_types/auth/authStoreSlice.d.ts.map +1 -1
- package/dist/_types/auth/hooks/useAuth.d.ts +0 -2
- package/dist/_types/auth/hooks/useAuth.d.ts.map +1 -1
- package/dist/_types/auth/index.d.ts +9 -1
- package/dist/_types/auth/index.d.ts.map +1 -1
- package/dist/_types/auth/pages/OtpInput.d.ts.map +1 -1
- package/dist/_types/auth/pages/SignUp/Email.d.ts +4 -0
- package/dist/_types/auth/pages/SignUp/Email.d.ts.map +1 -0
- package/dist/_types/auth/pages/SignUp/Google.d.ts +3 -0
- package/dist/_types/auth/pages/SignUp/Google.d.ts.map +1 -0
- package/dist/_types/auth/pages/SignUp/Passkey.d.ts +3 -0
- package/dist/_types/auth/pages/SignUp/Passkey.d.ts.map +1 -0
- package/dist/_types/auth/pages/SignUp/context.d.ts +25 -0
- package/dist/_types/auth/pages/SignUp/context.d.ts.map +1 -0
- package/dist/_types/auth/pages/SignUp/index.d.ts +31 -0
- package/dist/_types/auth/pages/SignUp/index.d.ts.map +1 -0
- package/dist/_types/auth/pages/Verifying.d.ts.map +1 -1
- package/dist/_types/auth/types.d.ts +0 -8
- package/dist/_types/auth/types.d.ts.map +1 -1
- package/dist/_types/auth/utils/isCancellationError.d.ts +2 -0
- package/dist/_types/auth/utils/isCancellationError.d.ts.map +1 -0
- package/dist/_types/connector.d.ts +3 -13
- package/dist/_types/connector.d.ts.map +1 -1
- package/dist/_types/index.d.ts +4 -3
- package/dist/_types/index.d.ts.map +1 -1
- package/dist/_types/shared/utils/common.d.ts +0 -1
- package/dist/_types/shared/utils/common.d.ts.map +1 -1
- package/dist/_types/store.d.ts +1 -6
- package/dist/_types/store.d.ts.map +1 -1
- package/dist/index.cjs +3 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +555 -562
- package/dist/index.mjs.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +4 -4
- package/src/auth/authStoreSlice.ts +7 -13
- package/src/auth/hooks/useAuth.ts +0 -5
- package/src/auth/index.tsx +19 -7
- package/src/auth/pages/OtpInput.tsx +1 -4
- package/src/auth/pages/SignUp/Email.tsx +86 -0
- package/src/auth/pages/SignUp/Google.tsx +43 -0
- package/src/auth/pages/SignUp/Passkey.tsx +56 -0
- package/src/auth/pages/SignUp/context.tsx +41 -0
- package/src/auth/pages/SignUp/index.tsx +138 -0
- package/src/auth/pages/Verifying.tsx +3 -10
- package/src/auth/types.ts +0 -9
- package/src/auth/utils/isCancellationError.ts +8 -0
- package/src/connector.ts +12 -28
- package/src/index.ts +3 -3
- package/src/shared/utils/common.ts +0 -12
- package/src/store.ts +1 -8
- package/dist/_types/auth/pages/SignUp.d.ts +0 -2
- package/dist/_types/auth/pages/SignUp.d.ts.map +0 -1
- package/src/auth/pages/SignUp.tsx +0 -311
package/src/index.ts
CHANGED
|
@@ -4,14 +4,14 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
// Auth
|
|
7
|
-
export {
|
|
7
|
+
export { ConnectWallet } from './auth'
|
|
8
8
|
export { useAuth } from './auth/hooks/useAuth'
|
|
9
|
-
export
|
|
9
|
+
export { SignUp } from './auth/pages/SignUp'
|
|
10
|
+
export type { AuthMethod, AuthStep, EmailAuthMethod } from './auth/types'
|
|
10
11
|
|
|
11
12
|
// Connector
|
|
12
13
|
export type {
|
|
13
14
|
// SigningConfig,
|
|
14
|
-
ZeroDevKitConfig,
|
|
15
15
|
ZeroDevKitConnectorParams,
|
|
16
16
|
} from './connector.js'
|
|
17
17
|
export { zeroDevWallet } from './connector.js'
|
|
@@ -8,18 +8,6 @@ export function shortenHex(hex: string, length = 4) {
|
|
|
8
8
|
return `${hex.slice(0, length + 2)}...${hex.slice(-length)}`
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
export function camelCaseToTitle(str: string): string {
|
|
12
|
-
if (str.length === 0 || str.at(0) === str.at(0)?.toUpperCase()) {
|
|
13
|
-
return str
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const words = str.replace(/([a-z])([A-Z])/g, '$1 $2').trim()
|
|
17
|
-
return words
|
|
18
|
-
.split(' ')
|
|
19
|
-
.map((word) => capitalizeFirst(word))
|
|
20
|
-
.join(' ')
|
|
21
|
-
}
|
|
22
|
-
|
|
23
11
|
const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
|
|
24
12
|
export function isValidEmailAddress(email: string): boolean {
|
|
25
13
|
return EMAIL_REGEX.test(email.trim())
|
package/src/store.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { ReactNode } from 'react'
|
|
2
1
|
import { create } from 'zustand'
|
|
3
2
|
import { subscribeWithSelector } from 'zustand/middleware'
|
|
4
3
|
import {
|
|
@@ -10,23 +9,17 @@ import type { PendingRequest } from './types.js'
|
|
|
10
9
|
export type State = {
|
|
11
10
|
pendingRequests: PendingRequest[]
|
|
12
11
|
userConfirmationListenerActive: boolean
|
|
13
|
-
logo: ReactNode | null
|
|
14
12
|
addPendingRequest: (request: PendingRequest) => void
|
|
15
13
|
removePendingRequest: (id: string) => void
|
|
16
14
|
clearPendingRequests: () => void
|
|
17
15
|
setUserConfirmationListenerActive: (active: boolean) => void
|
|
18
16
|
} & AuthStoreSlice
|
|
19
17
|
|
|
20
|
-
export
|
|
21
|
-
logo?: ReactNode
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export const createStore = (options: CreateStoreOptions = {}) =>
|
|
18
|
+
export const createStore = () =>
|
|
25
19
|
create<State>()(
|
|
26
20
|
subscribeWithSelector((set, get, store) => ({
|
|
27
21
|
pendingRequests: [],
|
|
28
22
|
userConfirmationListenerActive: false,
|
|
29
|
-
logo: options.logo ?? null,
|
|
30
23
|
addPendingRequest: (request) =>
|
|
31
24
|
set((state) => ({
|
|
32
25
|
pendingRequests: [...state.pendingRequests, request],
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"SignUp.d.ts","sourceRoot":"","sources":["../../../../src/auth/pages/SignUp.tsx"],"names":[],"mappings":"AA+BA,wBAAgB,MAAM,gCAuRrB"}
|
|
@@ -1,311 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Button,
|
|
3
|
-
Icon,
|
|
4
|
-
Input,
|
|
5
|
-
ListItem,
|
|
6
|
-
ListItemChevron,
|
|
7
|
-
ListItemIcon,
|
|
8
|
-
Text,
|
|
9
|
-
} from '@zerodev/react-ui'
|
|
10
|
-
import {
|
|
11
|
-
useAuthenticateOAuth,
|
|
12
|
-
useLoginPasskey,
|
|
13
|
-
useRegisterPasskey,
|
|
14
|
-
useSendMagicLink,
|
|
15
|
-
useSendOTP,
|
|
16
|
-
} from '@zerodev/wallet-react'
|
|
17
|
-
import { useState } from 'react'
|
|
18
|
-
import { SignUpFooter } from '../../shared/components/SignUpFooter'
|
|
19
|
-
import { isValidEmailAddress } from '../../shared/utils/common'
|
|
20
|
-
import { BlobAnimation } from '../components/BlobAnimation'
|
|
21
|
-
import { useAuth } from '../hooks/useAuth'
|
|
22
|
-
|
|
23
|
-
function isCancellationError(err: unknown): boolean {
|
|
24
|
-
if (!(err instanceof Error)) return false
|
|
25
|
-
// WebAuthn / passkey
|
|
26
|
-
if (err.name === 'AbortError' || err.name === 'NotAllowedError') return true
|
|
27
|
-
// OAuth (existing logic, message-based)
|
|
28
|
-
const msg = err.message.toLowerCase()
|
|
29
|
-
return msg.includes('oauth popup was closed')
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export function SignUp() {
|
|
33
|
-
const { goToStep, setEmail, setOtpSession, config, enabledMethods } =
|
|
34
|
-
useAuth()
|
|
35
|
-
const [agreedToTerms, setAgreedToTerms] = useState(false)
|
|
36
|
-
const [highlightAgreement, setHighlightAgreement] = useState(false)
|
|
37
|
-
const [emailInput, setEmailInput] = useState('')
|
|
38
|
-
const shouldUseOtp = config?.emailAuthMethod === 'otp'
|
|
39
|
-
const { mutateAsync: sendOtp, isPending: isSendOtpPending } = useSendOTP()
|
|
40
|
-
const { mutateAsync: sendMagicLink, isPending: isSendMagicLinkPending } =
|
|
41
|
-
useSendMagicLink()
|
|
42
|
-
const isEmailLoading = isSendOtpPending || isSendMagicLinkPending
|
|
43
|
-
|
|
44
|
-
const [error, setError] = useState<string | null>(null)
|
|
45
|
-
|
|
46
|
-
const { mutateAsync: authenticateOAuth, isPending: isGoogleLoading } =
|
|
47
|
-
useAuthenticateOAuth({
|
|
48
|
-
mutation: {
|
|
49
|
-
onSuccess: async () => {
|
|
50
|
-
goToStep('authenticated')
|
|
51
|
-
config?.onSuccess?.()
|
|
52
|
-
},
|
|
53
|
-
onError: (err) => {
|
|
54
|
-
config?.onError?.(err)
|
|
55
|
-
},
|
|
56
|
-
},
|
|
57
|
-
})
|
|
58
|
-
|
|
59
|
-
const { mutate: registerPasskey, isPending: isRegisterPasskeyPending } =
|
|
60
|
-
useRegisterPasskey({
|
|
61
|
-
mutation: {
|
|
62
|
-
onSuccess: () => {
|
|
63
|
-
goToStep('authenticated')
|
|
64
|
-
config?.onSuccess?.()
|
|
65
|
-
},
|
|
66
|
-
onError: (err) => {
|
|
67
|
-
if (!isCancellationError(err)) {
|
|
68
|
-
setError(err instanceof Error ? err.message : String(err))
|
|
69
|
-
}
|
|
70
|
-
config?.onError?.(err)
|
|
71
|
-
},
|
|
72
|
-
},
|
|
73
|
-
})
|
|
74
|
-
|
|
75
|
-
const { mutate: loginPasskey, isPending: isLoginPasskeyPending } =
|
|
76
|
-
useLoginPasskey({
|
|
77
|
-
mutation: {
|
|
78
|
-
onSuccess: () => {
|
|
79
|
-
goToStep('authenticated')
|
|
80
|
-
config?.onSuccess?.()
|
|
81
|
-
},
|
|
82
|
-
onError: (err) => {
|
|
83
|
-
if (!isCancellationError(err)) {
|
|
84
|
-
setError(err instanceof Error ? err.message : String(err))
|
|
85
|
-
}
|
|
86
|
-
config?.onError?.(err)
|
|
87
|
-
},
|
|
88
|
-
},
|
|
89
|
-
})
|
|
90
|
-
|
|
91
|
-
const anyPending =
|
|
92
|
-
isGoogleLoading ||
|
|
93
|
-
isEmailLoading ||
|
|
94
|
-
isRegisterPasskeyPending ||
|
|
95
|
-
isLoginPasskeyPending
|
|
96
|
-
const requiresAgreement = !!(
|
|
97
|
-
config?.termsAndConditionsUrl || config?.privacyPolicyUrl
|
|
98
|
-
)
|
|
99
|
-
const needsToAcceptAgreement = requiresAgreement && !agreedToTerms
|
|
100
|
-
|
|
101
|
-
const handleRegisterPasskey = () => {
|
|
102
|
-
if (anyPending) return
|
|
103
|
-
if (needsToAcceptAgreement) {
|
|
104
|
-
setHighlightAgreement(true)
|
|
105
|
-
return
|
|
106
|
-
}
|
|
107
|
-
setError(null)
|
|
108
|
-
registerPasskey()
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
const handleLoginPasskey = () => {
|
|
112
|
-
if (anyPending) return
|
|
113
|
-
if (needsToAcceptAgreement) {
|
|
114
|
-
setHighlightAgreement(true)
|
|
115
|
-
return
|
|
116
|
-
}
|
|
117
|
-
setError(null)
|
|
118
|
-
loginPasskey()
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
const handleGoogleAuth = async () => {
|
|
122
|
-
if (needsToAcceptAgreement) {
|
|
123
|
-
setHighlightAgreement(true)
|
|
124
|
-
return
|
|
125
|
-
}
|
|
126
|
-
setError(null)
|
|
127
|
-
try {
|
|
128
|
-
await authenticateOAuth({ provider: 'google' })
|
|
129
|
-
} catch (err) {
|
|
130
|
-
const message = err instanceof Error ? err.message : String(err)
|
|
131
|
-
if (!isCancellationError(err)) {
|
|
132
|
-
setError(message)
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
const handleEmailOtp = async () => {
|
|
138
|
-
if (!emailInput || anyPending) return
|
|
139
|
-
if (!isValidEmailAddress(emailInput)) return
|
|
140
|
-
if (needsToAcceptAgreement) {
|
|
141
|
-
setHighlightAgreement(true)
|
|
142
|
-
return
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
setError(null)
|
|
146
|
-
try {
|
|
147
|
-
const { otpId, otpEncryptionTargetBundle } = await sendOtp({
|
|
148
|
-
email: emailInput,
|
|
149
|
-
})
|
|
150
|
-
setEmail(emailInput)
|
|
151
|
-
setOtpSession({ otpId, otpEncryptionTargetBundle })
|
|
152
|
-
goToStep('otp-input')
|
|
153
|
-
} catch (err) {
|
|
154
|
-
setError(
|
|
155
|
-
err instanceof Error ? err.message : 'Failed to send verification code',
|
|
156
|
-
)
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
const handleEmailMagicLink = async () => {
|
|
161
|
-
if (!emailInput || anyPending) return
|
|
162
|
-
if (!isValidEmailAddress(emailInput)) return
|
|
163
|
-
if (needsToAcceptAgreement) {
|
|
164
|
-
setHighlightAgreement(true)
|
|
165
|
-
return
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
setError(null)
|
|
169
|
-
try {
|
|
170
|
-
const { otpId, otpEncryptionTargetBundle } = await sendMagicLink({
|
|
171
|
-
email: emailInput,
|
|
172
|
-
})
|
|
173
|
-
setEmail(emailInput)
|
|
174
|
-
setOtpSession({ otpId, otpEncryptionTargetBundle })
|
|
175
|
-
goToStep('email-verification')
|
|
176
|
-
} catch (err) {
|
|
177
|
-
setError(
|
|
178
|
-
err instanceof Error ? err.message : 'Failed to send verification code',
|
|
179
|
-
)
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
const handleEmailSubmit = shouldUseOtp ? handleEmailOtp : handleEmailMagicLink
|
|
184
|
-
|
|
185
|
-
if (error) {
|
|
186
|
-
return (
|
|
187
|
-
<div className="zd:flex zd:items-center zd:justify-center zd:h-full">
|
|
188
|
-
<div className="zd:flex zd:flex-col zd:gap-4 zd:max-w-md">
|
|
189
|
-
<Text className="zd:text-h2 zd:text-center">Error occurred</Text>
|
|
190
|
-
<Text className="zd:text-center zd:text-red-500">{error}</Text>
|
|
191
|
-
<Button
|
|
192
|
-
action="primary"
|
|
193
|
-
text="Try again"
|
|
194
|
-
onClick={() => setError(null)}
|
|
195
|
-
/>
|
|
196
|
-
</div>
|
|
197
|
-
</div>
|
|
198
|
-
)
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
return (
|
|
202
|
-
<div className="zd:flex-1 zd:flex zd:flex-col zd:justify-between zd:pb-4 zd:overflow-y-auto zd:overflow-x-hidden">
|
|
203
|
-
<div className="zd:flex-1 zd:flex zd:flex-col zd:justify-center">
|
|
204
|
-
<div className="zd:px-4 zd:flex zd:flex-col zd:items-center">
|
|
205
|
-
<div className="zd:w-full zd:px-16 zd:py-4">
|
|
206
|
-
<BlobAnimation className="zd:w-full zd:pointer-events-none zd:select-none" />
|
|
207
|
-
</div>
|
|
208
|
-
<Text className="zd:text-h2 zd:text-center">
|
|
209
|
-
Continue to your wallet
|
|
210
|
-
</Text>
|
|
211
|
-
<Text className="zd:mt-2 zd:text-center zd:text-greyScale/50">
|
|
212
|
-
Choose a sign-in method to proceed
|
|
213
|
-
</Text>
|
|
214
|
-
</div>
|
|
215
|
-
<div className="zd:mt-6 zd:flex zd:flex-col zd:gap-4 zd:mb-4">
|
|
216
|
-
{enabledMethods.includes('passkey') && (
|
|
217
|
-
<>
|
|
218
|
-
<div className="zd:px-4 zd:flex zd:flex-col zd:gap-2">
|
|
219
|
-
<Button
|
|
220
|
-
action="secondary"
|
|
221
|
-
text="Create a passkey"
|
|
222
|
-
iconName="key"
|
|
223
|
-
trailIcon
|
|
224
|
-
disabled={anyPending}
|
|
225
|
-
onClick={handleRegisterPasskey}
|
|
226
|
-
/>
|
|
227
|
-
<Button
|
|
228
|
-
action="secondary"
|
|
229
|
-
text="Log in with passkey"
|
|
230
|
-
iconName="key"
|
|
231
|
-
trailIcon
|
|
232
|
-
disabled={anyPending}
|
|
233
|
-
onClick={handleLoginPasskey}
|
|
234
|
-
/>
|
|
235
|
-
</div>
|
|
236
|
-
<div className="zd:flex zd:items-center zd:gap-3">
|
|
237
|
-
<div className="zd:h-px zd:flex-1 zd:bg-greyScale/30" />
|
|
238
|
-
<Text className="zd:text-body3">or</Text>
|
|
239
|
-
<div className="zd:h-px zd:flex-1 zd:bg-greyScale/30" />
|
|
240
|
-
</div>
|
|
241
|
-
</>
|
|
242
|
-
)}
|
|
243
|
-
<div className="zd:px-4 zd:flex zd:flex-col zd:gap-2">
|
|
244
|
-
{enabledMethods.includes('google') && (
|
|
245
|
-
<ListItem
|
|
246
|
-
icon={<ListItemIcon name="google" />}
|
|
247
|
-
title="Google"
|
|
248
|
-
trailing={<ListItemChevron />}
|
|
249
|
-
className="zd:rounded-3xl"
|
|
250
|
-
disabled={anyPending}
|
|
251
|
-
onClick={handleGoogleAuth}
|
|
252
|
-
/>
|
|
253
|
-
)}
|
|
254
|
-
{enabledMethods.includes('email') && (
|
|
255
|
-
<Input
|
|
256
|
-
iconName="email"
|
|
257
|
-
placeholder="Enter your email"
|
|
258
|
-
value={emailInput}
|
|
259
|
-
onChange={(e) => setEmailInput(e.target.value)}
|
|
260
|
-
type="email"
|
|
261
|
-
autoCapitalize="none"
|
|
262
|
-
autoComplete="email"
|
|
263
|
-
disabled={anyPending}
|
|
264
|
-
variant="listItemStyle"
|
|
265
|
-
containerClassName="zd:rounded-3xl"
|
|
266
|
-
onKeyDown={(e) => {
|
|
267
|
-
if (e.key === 'Enter' && emailInput && !anyPending) {
|
|
268
|
-
handleEmailSubmit()
|
|
269
|
-
}
|
|
270
|
-
}}
|
|
271
|
-
>
|
|
272
|
-
{isEmailLoading ? (
|
|
273
|
-
<div className="zd:w-13 zd:h-13 zd:flex zd:items-center zd:justify-center">
|
|
274
|
-
<div className="zd:w-5 zd:h-5 zd:border-2 zd:border-solarOrange zd:border-t-transparent zd:rounded-full zd:animate-spin" />
|
|
275
|
-
</div>
|
|
276
|
-
) : (
|
|
277
|
-
<button
|
|
278
|
-
type="button"
|
|
279
|
-
disabled={
|
|
280
|
-
!isValidEmailAddress(emailInput) || needsToAcceptAgreement
|
|
281
|
-
}
|
|
282
|
-
className={`zd:w-13 zd:h-13 zd:rounded-2xl zd:flex zd:items-center zd:justify-center zd:transition-colors ${
|
|
283
|
-
isValidEmailAddress(emailInput) && !needsToAcceptAgreement
|
|
284
|
-
? 'zd:cursor-pointer'
|
|
285
|
-
: 'zd:cursor-not-allowed zd:opacity-50'
|
|
286
|
-
}`}
|
|
287
|
-
onClick={() => handleEmailSubmit()}
|
|
288
|
-
>
|
|
289
|
-
<Icon name="chevronRight" className="zd:text-greyScale" />
|
|
290
|
-
</button>
|
|
291
|
-
)}
|
|
292
|
-
</Input>
|
|
293
|
-
)}
|
|
294
|
-
</div>
|
|
295
|
-
</div>
|
|
296
|
-
</div>
|
|
297
|
-
<div className="zd:px-4">
|
|
298
|
-
<SignUpFooter
|
|
299
|
-
termsAndConditionsUrl={config?.termsAndConditionsUrl}
|
|
300
|
-
privacyPolicyUrl={config?.privacyPolicyUrl}
|
|
301
|
-
agreedToTerms={agreedToTerms}
|
|
302
|
-
setAgreedToTerms={(agreed) => {
|
|
303
|
-
setAgreedToTerms(agreed)
|
|
304
|
-
if (agreed) setHighlightAgreement(false)
|
|
305
|
-
}}
|
|
306
|
-
highlight={highlightAgreement}
|
|
307
|
-
/>
|
|
308
|
-
</div>
|
|
309
|
-
</div>
|
|
310
|
-
)
|
|
311
|
-
}
|