lens-convex-auth 0.1.0
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/LICENSE +21 -0
- package/README.md +357 -0
- package/dist/auth-Baitf7Ze.d.ts +102 -0
- package/dist/chunk-2UCSL2CL.js +526 -0
- package/dist/chunk-2UCSL2CL.js.map +1 -0
- package/dist/chunk-CDAKZCYM.js +22 -0
- package/dist/chunk-CDAKZCYM.js.map +1 -0
- package/dist/cli/index.js +36 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/convex/index.d.ts +25 -0
- package/dist/convex/index.js +35 -0
- package/dist/convex/index.js.map +1 -0
- package/dist/index.d.ts +58 -0
- package/dist/index.js +56 -0
- package/dist/index.js.map +1 -0
- package/dist/react/index.d.ts +83 -0
- package/dist/react/index.js +1271 -0
- package/dist/react/index.js.map +1 -0
- package/dist/server/index.d.ts +33 -0
- package/dist/server/index.js +78 -0
- package/dist/server/index.js.map +1 -0
- package/package.json +105 -0
|
@@ -0,0 +1,1271 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import {
|
|
3
|
+
CHALLENGE_MAX_AGE_MS,
|
|
4
|
+
CHALLENGE_REFRESH_MS,
|
|
5
|
+
OnboardingError,
|
|
6
|
+
TOKENS_CHANGED_EVENT,
|
|
7
|
+
authenticateOnboarding,
|
|
8
|
+
checkUsername,
|
|
9
|
+
createLensAccount,
|
|
10
|
+
createOnboardingClient,
|
|
11
|
+
isUsernameTaken,
|
|
12
|
+
opensWalletWithDeepLink,
|
|
13
|
+
requestOnboardingChallenge,
|
|
14
|
+
signInToCreatedAccount,
|
|
15
|
+
signLensChallenge,
|
|
16
|
+
uploadAccountMetadata,
|
|
17
|
+
usernameFormatError
|
|
18
|
+
} from "../chunk-2UCSL2CL.js";
|
|
19
|
+
|
|
20
|
+
// src/react/provider.tsx
|
|
21
|
+
import * as React4 from "react";
|
|
22
|
+
import { LensProvider } from "@lens-protocol/react";
|
|
23
|
+
import { ThirdwebProvider, useActiveAccount as useActiveAccount2, useActiveWallet as useActiveWallet2, useDisconnect } from "thirdweb/react";
|
|
24
|
+
import { ConvexProviderWithAuth, useConvexAuth } from "convex/react";
|
|
25
|
+
|
|
26
|
+
// src/react/AccountDialog.tsx
|
|
27
|
+
import * as React3 from "react";
|
|
28
|
+
import { useAccountsAvailable } from "@lens-protocol/react";
|
|
29
|
+
import { evmAddress } from "@lens-protocol/client";
|
|
30
|
+
import { Loader2 as Loader22 } from "lucide-react";
|
|
31
|
+
|
|
32
|
+
// src/react/CreateAccountForm.tsx
|
|
33
|
+
import * as React from "react";
|
|
34
|
+
import { useActiveAccount, useActiveWallet } from "thirdweb/react";
|
|
35
|
+
import { Check, Loader2 } from "lucide-react";
|
|
36
|
+
|
|
37
|
+
// src/react/utils.ts
|
|
38
|
+
import { clsx } from "clsx";
|
|
39
|
+
import { twMerge } from "tailwind-merge";
|
|
40
|
+
function cn(...inputs) {
|
|
41
|
+
return twMerge(clsx(inputs));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// src/react/ui/button.tsx
|
|
45
|
+
import { Slot } from "@radix-ui/react-slot";
|
|
46
|
+
import { cva } from "class-variance-authority";
|
|
47
|
+
import { jsx } from "react/jsx-runtime";
|
|
48
|
+
var buttonVariants = cva(
|
|
49
|
+
"cursor-pointer inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
|
50
|
+
{
|
|
51
|
+
variants: {
|
|
52
|
+
variant: {
|
|
53
|
+
default: "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
|
|
54
|
+
destructive: "bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
|
|
55
|
+
outline: "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
|
|
56
|
+
secondary: "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
|
|
57
|
+
ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
|
|
58
|
+
link: "text-primary underline-offset-4 hover:underline"
|
|
59
|
+
},
|
|
60
|
+
size: {
|
|
61
|
+
default: "h-9 px-4 py-2 has-[>svg]:px-3",
|
|
62
|
+
sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
|
|
63
|
+
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
|
|
64
|
+
xl: "h-[50px] rounded-md px-8 has-[>svg]:px-6",
|
|
65
|
+
icon: "size-9"
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
defaultVariants: {
|
|
69
|
+
variant: "default",
|
|
70
|
+
size: "default"
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
);
|
|
74
|
+
function Button({
|
|
75
|
+
className,
|
|
76
|
+
variant,
|
|
77
|
+
size,
|
|
78
|
+
asChild = false,
|
|
79
|
+
...props
|
|
80
|
+
}) {
|
|
81
|
+
const Comp = asChild ? Slot : "button";
|
|
82
|
+
return /* @__PURE__ */ jsx(
|
|
83
|
+
Comp,
|
|
84
|
+
{
|
|
85
|
+
"data-slot": "button",
|
|
86
|
+
className: cn(buttonVariants({ variant, size, className })),
|
|
87
|
+
...props
|
|
88
|
+
}
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// src/react/ui/input.tsx
|
|
93
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
94
|
+
function Input({ className, type, ...props }) {
|
|
95
|
+
return /* @__PURE__ */ jsx2(
|
|
96
|
+
"input",
|
|
97
|
+
{
|
|
98
|
+
type,
|
|
99
|
+
"data-slot": "input",
|
|
100
|
+
className: cn(
|
|
101
|
+
"file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
|
102
|
+
"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
|
|
103
|
+
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
|
104
|
+
className
|
|
105
|
+
),
|
|
106
|
+
...props
|
|
107
|
+
}
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// src/react/ui/textarea.tsx
|
|
112
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
113
|
+
function Textarea({ className, ...props }) {
|
|
114
|
+
return /* @__PURE__ */ jsx3(
|
|
115
|
+
"textarea",
|
|
116
|
+
{
|
|
117
|
+
"data-slot": "textarea",
|
|
118
|
+
className: cn(
|
|
119
|
+
"flex field-sizing-content min-h-16 w-full rounded-md border border-input bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:aria-invalid:ring-destructive/40",
|
|
120
|
+
className
|
|
121
|
+
),
|
|
122
|
+
...props
|
|
123
|
+
}
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// src/react/ui/avatar.tsx
|
|
128
|
+
import * as AvatarPrimitive from "@radix-ui/react-avatar";
|
|
129
|
+
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
130
|
+
function Avatar({
|
|
131
|
+
className,
|
|
132
|
+
...props
|
|
133
|
+
}) {
|
|
134
|
+
return /* @__PURE__ */ jsx4(
|
|
135
|
+
AvatarPrimitive.Root,
|
|
136
|
+
{
|
|
137
|
+
"data-slot": "avatar",
|
|
138
|
+
className: cn(
|
|
139
|
+
"relative flex size-8 shrink-0 overflow-hidden rounded-full",
|
|
140
|
+
className
|
|
141
|
+
),
|
|
142
|
+
...props
|
|
143
|
+
}
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
function AvatarImage({
|
|
147
|
+
className,
|
|
148
|
+
...props
|
|
149
|
+
}) {
|
|
150
|
+
return /* @__PURE__ */ jsx4(
|
|
151
|
+
AvatarPrimitive.Image,
|
|
152
|
+
{
|
|
153
|
+
"data-slot": "avatar-image",
|
|
154
|
+
className: cn("aspect-square size-full", className),
|
|
155
|
+
...props
|
|
156
|
+
}
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
function AvatarFallback({
|
|
160
|
+
className,
|
|
161
|
+
...props
|
|
162
|
+
}) {
|
|
163
|
+
return /* @__PURE__ */ jsx4(
|
|
164
|
+
AvatarPrimitive.Fallback,
|
|
165
|
+
{
|
|
166
|
+
"data-slot": "avatar-fallback",
|
|
167
|
+
className: cn(
|
|
168
|
+
"bg-muted flex size-full items-center justify-center rounded-full",
|
|
169
|
+
className
|
|
170
|
+
),
|
|
171
|
+
...props
|
|
172
|
+
}
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// src/react/ui/field.tsx
|
|
177
|
+
import { useMemo } from "react";
|
|
178
|
+
import { cva as cva2 } from "class-variance-authority";
|
|
179
|
+
|
|
180
|
+
// src/react/ui/label.tsx
|
|
181
|
+
import * as LabelPrimitive from "@radix-ui/react-label";
|
|
182
|
+
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
183
|
+
function Label({
|
|
184
|
+
className,
|
|
185
|
+
...props
|
|
186
|
+
}) {
|
|
187
|
+
return /* @__PURE__ */ jsx5(
|
|
188
|
+
LabelPrimitive.Root,
|
|
189
|
+
{
|
|
190
|
+
"data-slot": "label",
|
|
191
|
+
className: cn(
|
|
192
|
+
"flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
|
|
193
|
+
className
|
|
194
|
+
),
|
|
195
|
+
...props
|
|
196
|
+
}
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// src/react/ui/field.tsx
|
|
201
|
+
import { jsx as jsx6, jsxs } from "react/jsx-runtime";
|
|
202
|
+
function FieldGroup({ className, ...props }) {
|
|
203
|
+
return /* @__PURE__ */ jsx6(
|
|
204
|
+
"div",
|
|
205
|
+
{
|
|
206
|
+
"data-slot": "field-group",
|
|
207
|
+
className: cn(
|
|
208
|
+
"group/field-group @container/field-group flex w-full flex-col gap-7 data-[slot=checkbox-group]:gap-3 [&>[data-slot=field-group]]:gap-4",
|
|
209
|
+
className
|
|
210
|
+
),
|
|
211
|
+
...props
|
|
212
|
+
}
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
var fieldVariants = cva2(
|
|
216
|
+
"group/field flex w-full gap-3 data-[invalid=true]:text-destructive",
|
|
217
|
+
{
|
|
218
|
+
variants: {
|
|
219
|
+
orientation: {
|
|
220
|
+
vertical: ["flex-col [&>*]:w-full [&>.sr-only]:w-auto"],
|
|
221
|
+
horizontal: [
|
|
222
|
+
"flex-row items-center",
|
|
223
|
+
"[&>[data-slot=field-label]]:flex-auto",
|
|
224
|
+
"has-[>[data-slot=field-content]]:items-start has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px"
|
|
225
|
+
],
|
|
226
|
+
responsive: [
|
|
227
|
+
"flex-col @md/field-group:flex-row @md/field-group:items-center [&>*]:w-full @md/field-group:[&>*]:w-auto [&>.sr-only]:w-auto",
|
|
228
|
+
"@md/field-group:[&>[data-slot=field-label]]:flex-auto",
|
|
229
|
+
"@md/field-group:has-[>[data-slot=field-content]]:items-start @md/field-group:has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px"
|
|
230
|
+
]
|
|
231
|
+
}
|
|
232
|
+
},
|
|
233
|
+
defaultVariants: {
|
|
234
|
+
orientation: "vertical"
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
);
|
|
238
|
+
function Field({
|
|
239
|
+
className,
|
|
240
|
+
orientation = "vertical",
|
|
241
|
+
...props
|
|
242
|
+
}) {
|
|
243
|
+
return /* @__PURE__ */ jsx6(
|
|
244
|
+
"div",
|
|
245
|
+
{
|
|
246
|
+
role: "group",
|
|
247
|
+
"data-slot": "field",
|
|
248
|
+
"data-orientation": orientation,
|
|
249
|
+
className: cn(fieldVariants({ orientation }), className),
|
|
250
|
+
...props
|
|
251
|
+
}
|
|
252
|
+
);
|
|
253
|
+
}
|
|
254
|
+
function FieldContent({ className, ...props }) {
|
|
255
|
+
return /* @__PURE__ */ jsx6(
|
|
256
|
+
"div",
|
|
257
|
+
{
|
|
258
|
+
"data-slot": "field-content",
|
|
259
|
+
className: cn(
|
|
260
|
+
"group/field-content flex flex-1 flex-col gap-1.5 leading-snug",
|
|
261
|
+
className
|
|
262
|
+
),
|
|
263
|
+
...props
|
|
264
|
+
}
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
function FieldLabel({
|
|
268
|
+
className,
|
|
269
|
+
...props
|
|
270
|
+
}) {
|
|
271
|
+
return /* @__PURE__ */ jsx6(
|
|
272
|
+
Label,
|
|
273
|
+
{
|
|
274
|
+
"data-slot": "field-label",
|
|
275
|
+
className: cn(
|
|
276
|
+
"group/field-label peer/field-label flex w-fit gap-2 leading-snug group-data-[disabled=true]/field:opacity-50",
|
|
277
|
+
"has-[>[data-slot=field]]:w-full has-[>[data-slot=field]]:flex-col has-[>[data-slot=field]]:rounded-md has-[>[data-slot=field]]:border [&>*]:data-[slot=field]:p-4",
|
|
278
|
+
"has-data-[state=checked]:border-primary has-data-[state=checked]:bg-primary/5 dark:has-data-[state=checked]:bg-primary/10",
|
|
279
|
+
className
|
|
280
|
+
),
|
|
281
|
+
...props
|
|
282
|
+
}
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
function FieldDescription({ className, ...props }) {
|
|
286
|
+
return /* @__PURE__ */ jsx6(
|
|
287
|
+
"p",
|
|
288
|
+
{
|
|
289
|
+
"data-slot": "field-description",
|
|
290
|
+
className: cn(
|
|
291
|
+
"text-sm leading-normal font-normal text-muted-foreground group-has-[[data-orientation=horizontal]]/field:text-balance",
|
|
292
|
+
"last:mt-0 nth-last-2:-mt-1 [[data-variant=legend]+&]:-mt-1.5",
|
|
293
|
+
"[&>a]:underline [&>a]:underline-offset-4 [&>a:hover]:text-primary",
|
|
294
|
+
className
|
|
295
|
+
),
|
|
296
|
+
...props
|
|
297
|
+
}
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// src/react/ui/input-group.tsx
|
|
302
|
+
import { cva as cva3 } from "class-variance-authority";
|
|
303
|
+
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
304
|
+
function InputGroup({ className, ...props }) {
|
|
305
|
+
return /* @__PURE__ */ jsx7(
|
|
306
|
+
"div",
|
|
307
|
+
{
|
|
308
|
+
"data-slot": "input-group",
|
|
309
|
+
role: "group",
|
|
310
|
+
className: cn(
|
|
311
|
+
"group/input-group relative flex w-full items-center rounded-md border border-input shadow-xs transition-[color,box-shadow] outline-none dark:bg-input/30",
|
|
312
|
+
"h-9 min-w-0 has-[>textarea]:h-auto",
|
|
313
|
+
// Variants based on alignment.
|
|
314
|
+
"has-[>[data-align=inline-start]]:[&>input]:pl-2",
|
|
315
|
+
"has-[>[data-align=inline-end]]:[&>input]:pr-2",
|
|
316
|
+
"has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>[data-align=block-start]]:[&>input]:pb-3",
|
|
317
|
+
"has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-end]]:[&>input]:pt-3",
|
|
318
|
+
// Focus state.
|
|
319
|
+
"has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:ring-[3px] has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50",
|
|
320
|
+
// Error state.
|
|
321
|
+
"has-[[data-slot][aria-invalid=true]]:border-destructive has-[[data-slot][aria-invalid=true]]:ring-destructive/20 dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40",
|
|
322
|
+
className
|
|
323
|
+
),
|
|
324
|
+
...props
|
|
325
|
+
}
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
var inputGroupAddonVariants = cva3(
|
|
329
|
+
"flex h-auto cursor-text items-center justify-center gap-2 py-1.5 text-sm font-medium text-muted-foreground select-none group-data-[disabled=true]/input-group:opacity-50 [&>kbd]:rounded-[calc(var(--radius)-5px)] [&>svg:not([class*='size-'])]:size-4",
|
|
330
|
+
{
|
|
331
|
+
variants: {
|
|
332
|
+
align: {
|
|
333
|
+
"inline-start": "order-first pl-3 has-[>button]:ml-[-0.45rem] has-[>kbd]:ml-[-0.35rem]",
|
|
334
|
+
"inline-end": "order-last pr-3 has-[>button]:mr-[-0.45rem] has-[>kbd]:mr-[-0.35rem]",
|
|
335
|
+
"block-start": "order-first w-full justify-start px-3 pt-3 group-has-[>input]/input-group:pt-2.5 [.border-b]:pb-3",
|
|
336
|
+
"block-end": "order-last w-full justify-start px-3 pb-3 group-has-[>input]/input-group:pb-2.5 [.border-t]:pt-3"
|
|
337
|
+
}
|
|
338
|
+
},
|
|
339
|
+
defaultVariants: {
|
|
340
|
+
align: "inline-start"
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
);
|
|
344
|
+
function InputGroupAddon({
|
|
345
|
+
className,
|
|
346
|
+
align = "inline-start",
|
|
347
|
+
...props
|
|
348
|
+
}) {
|
|
349
|
+
return /* @__PURE__ */ jsx7(
|
|
350
|
+
"div",
|
|
351
|
+
{
|
|
352
|
+
role: "group",
|
|
353
|
+
"data-slot": "input-group-addon",
|
|
354
|
+
"data-align": align,
|
|
355
|
+
className: cn(inputGroupAddonVariants({ align }), className),
|
|
356
|
+
onClick: (e) => {
|
|
357
|
+
if (e.target.closest("button")) {
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
e.currentTarget.parentElement?.querySelector("input")?.focus();
|
|
361
|
+
},
|
|
362
|
+
...props
|
|
363
|
+
}
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
var inputGroupButtonVariants = cva3(
|
|
367
|
+
"flex items-center gap-2 text-sm shadow-none",
|
|
368
|
+
{
|
|
369
|
+
variants: {
|
|
370
|
+
size: {
|
|
371
|
+
xs: "h-6 gap-1 rounded-[calc(var(--radius)-5px)] px-2 has-[>svg]:px-2 [&>svg:not([class*='size-'])]:size-3.5",
|
|
372
|
+
sm: "h-8 gap-1.5 rounded-md px-2.5 has-[>svg]:px-2.5",
|
|
373
|
+
"icon-xs": "size-6 rounded-[calc(var(--radius)-5px)] p-0 has-[>svg]:p-0",
|
|
374
|
+
"icon-sm": "size-8 p-0 has-[>svg]:p-0"
|
|
375
|
+
}
|
|
376
|
+
},
|
|
377
|
+
defaultVariants: {
|
|
378
|
+
size: "xs"
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
);
|
|
382
|
+
function InputGroupText({ className, ...props }) {
|
|
383
|
+
return /* @__PURE__ */ jsx7(
|
|
384
|
+
"span",
|
|
385
|
+
{
|
|
386
|
+
className: cn(
|
|
387
|
+
"flex items-center gap-2 text-sm text-muted-foreground [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4",
|
|
388
|
+
className
|
|
389
|
+
),
|
|
390
|
+
...props
|
|
391
|
+
}
|
|
392
|
+
);
|
|
393
|
+
}
|
|
394
|
+
function InputGroupInput({
|
|
395
|
+
className,
|
|
396
|
+
...props
|
|
397
|
+
}) {
|
|
398
|
+
return /* @__PURE__ */ jsx7(
|
|
399
|
+
Input,
|
|
400
|
+
{
|
|
401
|
+
"data-slot": "input-group-control",
|
|
402
|
+
className: cn(
|
|
403
|
+
"flex-1 rounded-none border-0 bg-transparent shadow-none focus-visible:ring-0 dark:bg-transparent",
|
|
404
|
+
className
|
|
405
|
+
),
|
|
406
|
+
...props
|
|
407
|
+
}
|
|
408
|
+
);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// src/react/CreateAccountForm.tsx
|
|
412
|
+
import { jsx as jsx8, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
413
|
+
var STEP_LABELS = {
|
|
414
|
+
signing: "Waiting for signature\u2026",
|
|
415
|
+
sign: "Sign in wallet",
|
|
416
|
+
checking: "Checking username\u2026",
|
|
417
|
+
uploading: "Uploading profile\u2026",
|
|
418
|
+
creating: "Creating account\u2026",
|
|
419
|
+
"signing-in": "Signing in\u2026"
|
|
420
|
+
};
|
|
421
|
+
var USERNAME_DEBOUNCE_MS = 300;
|
|
422
|
+
var MAX_PICTURE_BYTES = 10 * 1024 * 1024;
|
|
423
|
+
function CreateAccountForm({ ownerAddress, onCreated, onBack }) {
|
|
424
|
+
const { auth } = useLensAuth();
|
|
425
|
+
const activeAccount = useActiveAccount();
|
|
426
|
+
const wallet = useActiveWallet();
|
|
427
|
+
const [client] = React.useState(() => createOnboardingClient(auth));
|
|
428
|
+
const session = React.useRef(null);
|
|
429
|
+
const created = React.useRef(null);
|
|
430
|
+
const pictureInput = React.useRef(null);
|
|
431
|
+
const [username, setUsername] = React.useState("");
|
|
432
|
+
const [debouncedUsername, setDebouncedUsername] = React.useState("");
|
|
433
|
+
const [name, setName] = React.useState("");
|
|
434
|
+
const [bio, setBio] = React.useState("");
|
|
435
|
+
const [picture, setPicture] = React.useState(null);
|
|
436
|
+
const [pictureUrl, setPictureUrl] = React.useState(null);
|
|
437
|
+
const [availability, setAvailability] = React.useState(null);
|
|
438
|
+
const [usernameError, setUsernameError] = React.useState(null);
|
|
439
|
+
const [step, setStep] = React.useState(null);
|
|
440
|
+
const [error, setError] = React.useState(null);
|
|
441
|
+
const [prefetched, setPrefetched] = React.useState(null);
|
|
442
|
+
const formatError = username ? usernameFormatError(username) : null;
|
|
443
|
+
const busy = step !== null && step.status !== "sign";
|
|
444
|
+
React.useEffect(() => {
|
|
445
|
+
const refresh = () => {
|
|
446
|
+
if (document.visibilityState !== "visible" || session.current) return;
|
|
447
|
+
requestOnboardingChallenge(auth, client, ownerAddress).then((challenge) => setPrefetched({ challenge, fetchedAt: Date.now() })).catch(() => {
|
|
448
|
+
});
|
|
449
|
+
};
|
|
450
|
+
refresh();
|
|
451
|
+
const interval = setInterval(refresh, CHALLENGE_REFRESH_MS);
|
|
452
|
+
document.addEventListener("visibilitychange", refresh);
|
|
453
|
+
return () => {
|
|
454
|
+
clearInterval(interval);
|
|
455
|
+
document.removeEventListener("visibilitychange", refresh);
|
|
456
|
+
};
|
|
457
|
+
}, [auth, client, ownerAddress]);
|
|
458
|
+
React.useEffect(() => () => {
|
|
459
|
+
if (!created.current) void session.current?.logout();
|
|
460
|
+
}, []);
|
|
461
|
+
React.useEffect(() => {
|
|
462
|
+
if (!picture) return setPictureUrl(null);
|
|
463
|
+
const url = URL.createObjectURL(picture);
|
|
464
|
+
setPictureUrl(url);
|
|
465
|
+
return () => URL.revokeObjectURL(url);
|
|
466
|
+
}, [picture]);
|
|
467
|
+
React.useEffect(() => {
|
|
468
|
+
const timeout = setTimeout(() => setDebouncedUsername(username), USERNAME_DEBOUNCE_MS);
|
|
469
|
+
return () => clearTimeout(timeout);
|
|
470
|
+
}, [username]);
|
|
471
|
+
React.useEffect(() => {
|
|
472
|
+
if (!debouncedUsername || usernameFormatError(debouncedUsername)) return;
|
|
473
|
+
let cancelled = false;
|
|
474
|
+
setAvailability({ localName: debouncedUsername, status: "checking" });
|
|
475
|
+
isUsernameTaken(auth, debouncedUsername).then((taken) => !cancelled && setAvailability({ localName: debouncedUsername, status: taken ? "taken" : "available" })).catch(() => !cancelled && setAvailability({ localName: debouncedUsername, status: "unknown" }));
|
|
476
|
+
return () => {
|
|
477
|
+
cancelled = true;
|
|
478
|
+
};
|
|
479
|
+
}, [auth, debouncedUsername]);
|
|
480
|
+
const fail = (e) => {
|
|
481
|
+
setStep(null);
|
|
482
|
+
if (e?.name === "UnauthenticatedError") session.current = null;
|
|
483
|
+
setError(e instanceof OnboardingError ? e.message : "Couldn\u2019t create your account. Please try again.");
|
|
484
|
+
};
|
|
485
|
+
const create = async (onboarding) => {
|
|
486
|
+
const localName = username;
|
|
487
|
+
if (!created.current) {
|
|
488
|
+
setStep({ status: "checking" });
|
|
489
|
+
const reason = await checkUsername(onboarding, localName);
|
|
490
|
+
if (reason) {
|
|
491
|
+
setUsernameError({ localName, message: reason });
|
|
492
|
+
setStep(null);
|
|
493
|
+
return;
|
|
494
|
+
}
|
|
495
|
+
setStep({ status: "uploading" });
|
|
496
|
+
const metadataUri = await uploadAccountMetadata(auth, { name: name.trim(), bio: bio.trim(), picture });
|
|
497
|
+
setStep({ status: "creating" });
|
|
498
|
+
if (!wallet) throw new OnboardingError("Connect your wallet");
|
|
499
|
+
const account = await createLensAccount(auth, onboarding, wallet, { localName, metadataUri });
|
|
500
|
+
created.current = { address: account.address, localName: account.username?.localName ?? localName };
|
|
501
|
+
}
|
|
502
|
+
setStep({ status: "signing-in" });
|
|
503
|
+
await signInToCreatedAccount(auth, onboarding, created.current.address);
|
|
504
|
+
await onCreated(created.current);
|
|
505
|
+
};
|
|
506
|
+
const continueWith = (authenticating) => {
|
|
507
|
+
authenticating.then(async (onboarding) => {
|
|
508
|
+
if (!onboarding) return;
|
|
509
|
+
session.current = onboarding;
|
|
510
|
+
await create(onboarding);
|
|
511
|
+
}).catch(fail);
|
|
512
|
+
};
|
|
513
|
+
const readyChallenge = () => {
|
|
514
|
+
if (step?.status === "sign") return step.challenge;
|
|
515
|
+
if (!prefetched || Date.now() - prefetched.fetchedAt >= CHALLENGE_MAX_AGE_MS) return null;
|
|
516
|
+
return prefetched.challenge;
|
|
517
|
+
};
|
|
518
|
+
const handleSubmit = (e) => {
|
|
519
|
+
e.preventDefault();
|
|
520
|
+
if (busy || !activeAccount) return;
|
|
521
|
+
if (!username) return setUsernameError({ localName: username, message: "Choose a username" });
|
|
522
|
+
if (formatError || availability?.status === "taken") return;
|
|
523
|
+
setError(null);
|
|
524
|
+
setUsernameError(null);
|
|
525
|
+
if (session.current) {
|
|
526
|
+
setStep({ status: "checking" });
|
|
527
|
+
create(session.current).catch(fail);
|
|
528
|
+
return;
|
|
529
|
+
}
|
|
530
|
+
const challenge = readyChallenge();
|
|
531
|
+
if (challenge) {
|
|
532
|
+
const signing = signLensChallenge(challenge, activeAccount);
|
|
533
|
+
setPrefetched(null);
|
|
534
|
+
setStep({ status: "signing" });
|
|
535
|
+
continueWith(signing.then((signature) => authenticateOnboarding(client, challenge, signature)));
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
538
|
+
setStep({ status: "signing" });
|
|
539
|
+
continueWith((async () => {
|
|
540
|
+
const fetched = await requestOnboardingChallenge(auth, client, ownerAddress);
|
|
541
|
+
if (opensWalletWithDeepLink()) {
|
|
542
|
+
setStep({ status: "sign", challenge: fetched });
|
|
543
|
+
return null;
|
|
544
|
+
}
|
|
545
|
+
const signature = await signLensChallenge(fetched, activeAccount);
|
|
546
|
+
return authenticateOnboarding(client, fetched, signature);
|
|
547
|
+
})());
|
|
548
|
+
};
|
|
549
|
+
const handlePictureChange = (e) => {
|
|
550
|
+
const file = e.target.files?.[0] ?? null;
|
|
551
|
+
e.target.value = "";
|
|
552
|
+
if (file && file.size > MAX_PICTURE_BYTES) return setError("Choose a picture under 10 MB");
|
|
553
|
+
setError(null);
|
|
554
|
+
setPicture(file);
|
|
555
|
+
};
|
|
556
|
+
const usernameMessage = (() => {
|
|
557
|
+
if (formatError) return { text: formatError, isError: true };
|
|
558
|
+
if (usernameError?.localName === username) return { text: usernameError.message, isError: true };
|
|
559
|
+
if (availability?.localName !== username) return null;
|
|
560
|
+
switch (availability.status) {
|
|
561
|
+
case "checking":
|
|
562
|
+
return { text: "Checking availability\u2026", isError: false };
|
|
563
|
+
case "available":
|
|
564
|
+
return { text: "Available", isError: false };
|
|
565
|
+
case "taken":
|
|
566
|
+
return { text: "This username is taken", isError: true };
|
|
567
|
+
default:
|
|
568
|
+
return null;
|
|
569
|
+
}
|
|
570
|
+
})();
|
|
571
|
+
const fallbackInitial = (name.trim() || username || "?").charAt(0).toUpperCase();
|
|
572
|
+
const usernameStatus = availability?.localName === username && !formatError ? availability.status : null;
|
|
573
|
+
return /* @__PURE__ */ jsx8("form", { className: "py-4", onSubmit: handleSubmit, noValidate: true, children: /* @__PURE__ */ jsxs2(FieldGroup, { className: "gap-5", children: [
|
|
574
|
+
/* @__PURE__ */ jsxs2(Field, { orientation: "horizontal", children: [
|
|
575
|
+
/* @__PURE__ */ jsxs2(Avatar, { className: "size-16", children: [
|
|
576
|
+
pictureUrl && /* @__PURE__ */ jsx8(AvatarImage, { src: pictureUrl, alt: "", className: "object-cover" }),
|
|
577
|
+
/* @__PURE__ */ jsx8(AvatarFallback, { className: "text-lg font-semibold", children: fallbackInitial })
|
|
578
|
+
] }),
|
|
579
|
+
/* @__PURE__ */ jsxs2(FieldContent, { children: [
|
|
580
|
+
/* @__PURE__ */ jsxs2(FieldLabel, { htmlFor: "create-account-picture", children: [
|
|
581
|
+
"Profile picture ",
|
|
582
|
+
/* @__PURE__ */ jsx8("span", { className: "font-normal text-muted-foreground", children: "(optional)" })
|
|
583
|
+
] }),
|
|
584
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex gap-2", children: [
|
|
585
|
+
/* @__PURE__ */ jsx8(Button, { type: "button", variant: "outline", size: "sm", onClick: () => pictureInput.current?.click(), disabled: busy, children: picture ? "Change" : "Add photo" }),
|
|
586
|
+
picture && /* @__PURE__ */ jsx8(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => setPicture(null), disabled: busy, children: "Remove" })
|
|
587
|
+
] })
|
|
588
|
+
] }),
|
|
589
|
+
/* @__PURE__ */ jsx8(
|
|
590
|
+
"input",
|
|
591
|
+
{
|
|
592
|
+
ref: pictureInput,
|
|
593
|
+
id: "create-account-picture",
|
|
594
|
+
type: "file",
|
|
595
|
+
accept: "image/*",
|
|
596
|
+
className: "sr-only",
|
|
597
|
+
onChange: handlePictureChange,
|
|
598
|
+
disabled: busy
|
|
599
|
+
}
|
|
600
|
+
)
|
|
601
|
+
] }),
|
|
602
|
+
/* @__PURE__ */ jsxs2(Field, { "data-invalid": usernameMessage?.isError || void 0, children: [
|
|
603
|
+
/* @__PURE__ */ jsx8(FieldLabel, { htmlFor: "create-account-username", children: "Username" }),
|
|
604
|
+
/* @__PURE__ */ jsxs2(InputGroup, { children: [
|
|
605
|
+
/* @__PURE__ */ jsx8(InputGroupAddon, { children: /* @__PURE__ */ jsx8(InputGroupText, { children: "@" }) }),
|
|
606
|
+
/* @__PURE__ */ jsx8(
|
|
607
|
+
InputGroupInput,
|
|
608
|
+
{
|
|
609
|
+
id: "create-account-username",
|
|
610
|
+
value: username,
|
|
611
|
+
onChange: (e) => setUsername(e.target.value.trim().toLowerCase()),
|
|
612
|
+
autoComplete: "off",
|
|
613
|
+
autoCapitalize: "none",
|
|
614
|
+
autoCorrect: "off",
|
|
615
|
+
spellCheck: false,
|
|
616
|
+
"aria-invalid": usernameMessage?.isError || void 0,
|
|
617
|
+
"aria-describedby": "create-account-username-status",
|
|
618
|
+
disabled: busy
|
|
619
|
+
}
|
|
620
|
+
),
|
|
621
|
+
/* @__PURE__ */ jsxs2(InputGroupAddon, { align: "inline-end", children: [
|
|
622
|
+
usernameStatus === "checking" && /* @__PURE__ */ jsx8(Loader2, { className: "animate-spin" }),
|
|
623
|
+
usernameStatus === "available" && /* @__PURE__ */ jsx8(Check, {})
|
|
624
|
+
] })
|
|
625
|
+
] }),
|
|
626
|
+
/* @__PURE__ */ jsx8(
|
|
627
|
+
FieldDescription,
|
|
628
|
+
{
|
|
629
|
+
id: "create-account-username-status",
|
|
630
|
+
"aria-live": "polite",
|
|
631
|
+
className: cn("min-h-5 leading-5", usernameMessage?.isError && "text-destructive"),
|
|
632
|
+
children: usernameMessage?.text
|
|
633
|
+
}
|
|
634
|
+
)
|
|
635
|
+
] }),
|
|
636
|
+
/* @__PURE__ */ jsxs2(Field, { children: [
|
|
637
|
+
/* @__PURE__ */ jsxs2(FieldLabel, { htmlFor: "create-account-name", children: [
|
|
638
|
+
"Name ",
|
|
639
|
+
/* @__PURE__ */ jsx8("span", { className: "font-normal text-muted-foreground", children: "(optional)" })
|
|
640
|
+
] }),
|
|
641
|
+
/* @__PURE__ */ jsx8(Input, { id: "create-account-name", value: name, onChange: (e) => setName(e.target.value), maxLength: 100, disabled: busy })
|
|
642
|
+
] }),
|
|
643
|
+
/* @__PURE__ */ jsxs2(Field, { children: [
|
|
644
|
+
/* @__PURE__ */ jsxs2(FieldLabel, { htmlFor: "create-account-bio", children: [
|
|
645
|
+
"Bio ",
|
|
646
|
+
/* @__PURE__ */ jsx8("span", { className: "font-normal text-muted-foreground", children: "(optional)" })
|
|
647
|
+
] }),
|
|
648
|
+
/* @__PURE__ */ jsx8(
|
|
649
|
+
Textarea,
|
|
650
|
+
{
|
|
651
|
+
id: "create-account-bio",
|
|
652
|
+
value: bio,
|
|
653
|
+
onChange: (e) => setBio(e.target.value),
|
|
654
|
+
maxLength: 300,
|
|
655
|
+
rows: 3,
|
|
656
|
+
className: "resize-none",
|
|
657
|
+
disabled: busy
|
|
658
|
+
}
|
|
659
|
+
)
|
|
660
|
+
] }),
|
|
661
|
+
/* @__PURE__ */ jsxs2(Field, { className: "gap-2", children: [
|
|
662
|
+
/* @__PURE__ */ jsxs2(Button, { type: "submit", className: "w-full", disabled: busy || !activeAccount, children: [
|
|
663
|
+
busy && /* @__PURE__ */ jsx8(Loader2, { className: "animate-spin" }),
|
|
664
|
+
step ? STEP_LABELS[step.status] : "Create account"
|
|
665
|
+
] }),
|
|
666
|
+
/* @__PURE__ */ jsx8(FieldDescription, { "aria-live": "polite", className: cn("min-h-5 leading-5", error && "text-destructive"), children: error ?? (step?.status === "sign" ? "Tap again to sign in your wallet." : "You\u2019ll sign a message in your wallet. It\u2019s free.") }),
|
|
667
|
+
onBack && /* @__PURE__ */ jsx8(Button, { type: "button", variant: "ghost", className: "w-full", onClick: onBack, disabled: busy, children: "Back to your accounts" })
|
|
668
|
+
] })
|
|
669
|
+
] }) });
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
// src/react/dom.ts
|
|
673
|
+
import * as React2 from "react";
|
|
674
|
+
function useMediaQuery(query) {
|
|
675
|
+
return React2.useSyncExternalStore(
|
|
676
|
+
(onChange) => {
|
|
677
|
+
const media = window.matchMedia(query);
|
|
678
|
+
media.addEventListener("change", onChange);
|
|
679
|
+
return () => media.removeEventListener("change", onChange);
|
|
680
|
+
},
|
|
681
|
+
() => window.matchMedia(query).matches,
|
|
682
|
+
() => false
|
|
683
|
+
);
|
|
684
|
+
}
|
|
685
|
+
function readTheme() {
|
|
686
|
+
const classes = document.documentElement.classList;
|
|
687
|
+
if (classes.contains("dark")) return "dark";
|
|
688
|
+
if (classes.contains("light")) return "light";
|
|
689
|
+
return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
|
690
|
+
}
|
|
691
|
+
function useDocumentTheme() {
|
|
692
|
+
return React2.useSyncExternalStore(
|
|
693
|
+
(onChange) => {
|
|
694
|
+
const observer = new MutationObserver(onChange);
|
|
695
|
+
observer.observe(document.documentElement, { attributes: true, attributeFilter: ["class"] });
|
|
696
|
+
const media = window.matchMedia("(prefers-color-scheme: dark)");
|
|
697
|
+
media.addEventListener("change", onChange);
|
|
698
|
+
return () => {
|
|
699
|
+
observer.disconnect();
|
|
700
|
+
media.removeEventListener("change", onChange);
|
|
701
|
+
};
|
|
702
|
+
},
|
|
703
|
+
readTheme,
|
|
704
|
+
() => "light"
|
|
705
|
+
);
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
// src/react/ui/dialog.tsx
|
|
709
|
+
import * as DialogPrimitive from "@radix-ui/react-dialog";
|
|
710
|
+
import { XIcon } from "lucide-react";
|
|
711
|
+
import { jsx as jsx9, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
712
|
+
function Dialog({
|
|
713
|
+
...props
|
|
714
|
+
}) {
|
|
715
|
+
return /* @__PURE__ */ jsx9(DialogPrimitive.Root, { "data-slot": "dialog", ...props });
|
|
716
|
+
}
|
|
717
|
+
function DialogPortal({
|
|
718
|
+
...props
|
|
719
|
+
}) {
|
|
720
|
+
return /* @__PURE__ */ jsx9(DialogPrimitive.Portal, { "data-slot": "dialog-portal", ...props });
|
|
721
|
+
}
|
|
722
|
+
function DialogOverlay({
|
|
723
|
+
className,
|
|
724
|
+
...props
|
|
725
|
+
}) {
|
|
726
|
+
return /* @__PURE__ */ jsx9(
|
|
727
|
+
DialogPrimitive.Overlay,
|
|
728
|
+
{
|
|
729
|
+
"data-slot": "dialog-overlay",
|
|
730
|
+
className: cn(
|
|
731
|
+
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
|
|
732
|
+
className
|
|
733
|
+
),
|
|
734
|
+
...props
|
|
735
|
+
}
|
|
736
|
+
);
|
|
737
|
+
}
|
|
738
|
+
function DialogContent({
|
|
739
|
+
className,
|
|
740
|
+
children,
|
|
741
|
+
showCloseButton = true,
|
|
742
|
+
...props
|
|
743
|
+
}) {
|
|
744
|
+
return /* @__PURE__ */ jsxs3(DialogPortal, { "data-slot": "dialog-portal", children: [
|
|
745
|
+
/* @__PURE__ */ jsx9(DialogOverlay, {}),
|
|
746
|
+
/* @__PURE__ */ jsxs3(
|
|
747
|
+
DialogPrimitive.Content,
|
|
748
|
+
{
|
|
749
|
+
"data-slot": "dialog-content",
|
|
750
|
+
className: cn(
|
|
751
|
+
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg",
|
|
752
|
+
className
|
|
753
|
+
),
|
|
754
|
+
...props,
|
|
755
|
+
children: [
|
|
756
|
+
children,
|
|
757
|
+
showCloseButton && /* @__PURE__ */ jsxs3(
|
|
758
|
+
DialogPrimitive.Close,
|
|
759
|
+
{
|
|
760
|
+
"data-slot": "dialog-close",
|
|
761
|
+
className: "ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
762
|
+
children: [
|
|
763
|
+
/* @__PURE__ */ jsx9(XIcon, {}),
|
|
764
|
+
/* @__PURE__ */ jsx9("span", { className: "sr-only", children: "Close" })
|
|
765
|
+
]
|
|
766
|
+
}
|
|
767
|
+
)
|
|
768
|
+
]
|
|
769
|
+
}
|
|
770
|
+
)
|
|
771
|
+
] });
|
|
772
|
+
}
|
|
773
|
+
function DialogHeader({ className, ...props }) {
|
|
774
|
+
return /* @__PURE__ */ jsx9(
|
|
775
|
+
"div",
|
|
776
|
+
{
|
|
777
|
+
"data-slot": "dialog-header",
|
|
778
|
+
className: cn("flex flex-col gap-2 text-center sm:text-left", className),
|
|
779
|
+
...props
|
|
780
|
+
}
|
|
781
|
+
);
|
|
782
|
+
}
|
|
783
|
+
function DialogTitle({
|
|
784
|
+
className,
|
|
785
|
+
...props
|
|
786
|
+
}) {
|
|
787
|
+
return /* @__PURE__ */ jsx9(
|
|
788
|
+
DialogPrimitive.Title,
|
|
789
|
+
{
|
|
790
|
+
"data-slot": "dialog-title",
|
|
791
|
+
className: cn("text-lg leading-none font-semibold", className),
|
|
792
|
+
...props
|
|
793
|
+
}
|
|
794
|
+
);
|
|
795
|
+
}
|
|
796
|
+
function DialogDescription({
|
|
797
|
+
className,
|
|
798
|
+
...props
|
|
799
|
+
}) {
|
|
800
|
+
return /* @__PURE__ */ jsx9(
|
|
801
|
+
DialogPrimitive.Description,
|
|
802
|
+
{
|
|
803
|
+
"data-slot": "dialog-description",
|
|
804
|
+
className: cn("text-muted-foreground text-sm", className),
|
|
805
|
+
...props
|
|
806
|
+
}
|
|
807
|
+
);
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
// src/react/ui/drawer.tsx
|
|
811
|
+
import { Drawer as DrawerPrimitive } from "vaul";
|
|
812
|
+
import { jsx as jsx10, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
813
|
+
function Drawer({
|
|
814
|
+
...props
|
|
815
|
+
}) {
|
|
816
|
+
return /* @__PURE__ */ jsx10(DrawerPrimitive.Root, { "data-slot": "drawer", ...props });
|
|
817
|
+
}
|
|
818
|
+
function DrawerPortal({
|
|
819
|
+
...props
|
|
820
|
+
}) {
|
|
821
|
+
return /* @__PURE__ */ jsx10(DrawerPrimitive.Portal, { "data-slot": "drawer-portal", ...props });
|
|
822
|
+
}
|
|
823
|
+
function DrawerOverlay({
|
|
824
|
+
className,
|
|
825
|
+
...props
|
|
826
|
+
}) {
|
|
827
|
+
return /* @__PURE__ */ jsx10(
|
|
828
|
+
DrawerPrimitive.Overlay,
|
|
829
|
+
{
|
|
830
|
+
"data-slot": "drawer-overlay",
|
|
831
|
+
className: cn(
|
|
832
|
+
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
|
|
833
|
+
className
|
|
834
|
+
),
|
|
835
|
+
...props
|
|
836
|
+
}
|
|
837
|
+
);
|
|
838
|
+
}
|
|
839
|
+
function DrawerContent({
|
|
840
|
+
className,
|
|
841
|
+
children,
|
|
842
|
+
...props
|
|
843
|
+
}) {
|
|
844
|
+
return /* @__PURE__ */ jsxs4(DrawerPortal, { "data-slot": "drawer-portal", children: [
|
|
845
|
+
/* @__PURE__ */ jsx10(DrawerOverlay, {}),
|
|
846
|
+
/* @__PURE__ */ jsxs4(
|
|
847
|
+
DrawerPrimitive.Content,
|
|
848
|
+
{
|
|
849
|
+
"data-slot": "drawer-content",
|
|
850
|
+
className: cn(
|
|
851
|
+
"group/drawer-content bg-background fixed z-50 flex h-auto flex-col",
|
|
852
|
+
"data-[vaul-drawer-direction=top]:inset-x-0 data-[vaul-drawer-direction=top]:top-0 data-[vaul-drawer-direction=top]:mb-24 data-[vaul-drawer-direction=top]:max-h-[80vh] data-[vaul-drawer-direction=top]:rounded-b-lg data-[vaul-drawer-direction=top]:border-b",
|
|
853
|
+
"data-[vaul-drawer-direction=bottom]:inset-x-0 data-[vaul-drawer-direction=bottom]:bottom-0 data-[vaul-drawer-direction=bottom]:mt-24 data-[vaul-drawer-direction=bottom]:max-h-[80vh] data-[vaul-drawer-direction=bottom]:rounded-t-lg data-[vaul-drawer-direction=bottom]:border-t",
|
|
854
|
+
"data-[vaul-drawer-direction=right]:inset-y-0 data-[vaul-drawer-direction=right]:right-0 data-[vaul-drawer-direction=right]:w-3/4 data-[vaul-drawer-direction=right]:border-l data-[vaul-drawer-direction=right]:sm:max-w-sm",
|
|
855
|
+
"data-[vaul-drawer-direction=left]:inset-y-0 data-[vaul-drawer-direction=left]:left-0 data-[vaul-drawer-direction=left]:w-3/4 data-[vaul-drawer-direction=left]:border-r data-[vaul-drawer-direction=left]:sm:max-w-sm",
|
|
856
|
+
className
|
|
857
|
+
),
|
|
858
|
+
...props,
|
|
859
|
+
children: [
|
|
860
|
+
/* @__PURE__ */ jsx10("div", { className: "bg-muted mx-auto mt-4 hidden h-2 w-[100px] shrink-0 rounded-full group-data-[vaul-drawer-direction=bottom]/drawer-content:block" }),
|
|
861
|
+
children
|
|
862
|
+
]
|
|
863
|
+
}
|
|
864
|
+
)
|
|
865
|
+
] });
|
|
866
|
+
}
|
|
867
|
+
function DrawerHeader({ className, ...props }) {
|
|
868
|
+
return /* @__PURE__ */ jsx10(
|
|
869
|
+
"div",
|
|
870
|
+
{
|
|
871
|
+
"data-slot": "drawer-header",
|
|
872
|
+
className: cn(
|
|
873
|
+
"flex flex-col gap-0.5 p-4 group-data-[vaul-drawer-direction=bottom]/drawer-content:text-center group-data-[vaul-drawer-direction=top]/drawer-content:text-center md:gap-1.5 md:text-left",
|
|
874
|
+
className
|
|
875
|
+
),
|
|
876
|
+
...props
|
|
877
|
+
}
|
|
878
|
+
);
|
|
879
|
+
}
|
|
880
|
+
function DrawerTitle({
|
|
881
|
+
className,
|
|
882
|
+
...props
|
|
883
|
+
}) {
|
|
884
|
+
return /* @__PURE__ */ jsx10(
|
|
885
|
+
DrawerPrimitive.Title,
|
|
886
|
+
{
|
|
887
|
+
"data-slot": "drawer-title",
|
|
888
|
+
className: cn("text-foreground font-semibold", className),
|
|
889
|
+
...props
|
|
890
|
+
}
|
|
891
|
+
);
|
|
892
|
+
}
|
|
893
|
+
function DrawerDescription({
|
|
894
|
+
className,
|
|
895
|
+
...props
|
|
896
|
+
}) {
|
|
897
|
+
return /* @__PURE__ */ jsx10(
|
|
898
|
+
DrawerPrimitive.Description,
|
|
899
|
+
{
|
|
900
|
+
"data-slot": "drawer-description",
|
|
901
|
+
className: cn("text-muted-foreground text-sm", className),
|
|
902
|
+
...props
|
|
903
|
+
}
|
|
904
|
+
);
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
// src/react/AccountDialog.tsx
|
|
908
|
+
import { Fragment, jsx as jsx11, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
909
|
+
function AccountDialog() {
|
|
910
|
+
const { wallet, signOut } = useLensAuth();
|
|
911
|
+
const { open, required, setOpen } = useAccountDialogState();
|
|
912
|
+
const isDesktop = useMediaQuery("(min-width: 768px)");
|
|
913
|
+
const [creating, setCreating] = React3.useState(false);
|
|
914
|
+
React3.useEffect(() => {
|
|
915
|
+
if (!open) setCreating(false);
|
|
916
|
+
}, [open]);
|
|
917
|
+
const handleOpenChange = (next) => {
|
|
918
|
+
if (!next && required) return;
|
|
919
|
+
setOpen(next);
|
|
920
|
+
};
|
|
921
|
+
const body = wallet ? /* @__PURE__ */ jsx11(AccountDialogBody, { wallet, creating, setCreating }, wallet.address) : null;
|
|
922
|
+
const disconnect = /* @__PURE__ */ jsx11(Button, { onClick: signOut, variant: "outline", className: "w-full", children: "Disconnect Wallet" });
|
|
923
|
+
if (isDesktop) {
|
|
924
|
+
return /* @__PURE__ */ jsx11(Dialog, { open, onOpenChange: handleOpenChange, children: /* @__PURE__ */ jsxs5(
|
|
925
|
+
DialogContent,
|
|
926
|
+
{
|
|
927
|
+
className: "sm:max-w-[425px] max-h-[80vh] overflow-y-auto",
|
|
928
|
+
showCloseButton: !required,
|
|
929
|
+
onEscapeKeyDown: (e) => required && e.preventDefault(),
|
|
930
|
+
onPointerDownOutside: (e) => required && e.preventDefault(),
|
|
931
|
+
children: [
|
|
932
|
+
body,
|
|
933
|
+
/* @__PURE__ */ jsx11("div", { className: "mt-4", children: disconnect })
|
|
934
|
+
]
|
|
935
|
+
}
|
|
936
|
+
) });
|
|
937
|
+
}
|
|
938
|
+
return /* @__PURE__ */ jsx11(Drawer, { open, onOpenChange: handleOpenChange, dismissible: !required, children: /* @__PURE__ */ jsxs5(DrawerContent, { className: "max-h-[80vh] overflow-y-auto", children: [
|
|
939
|
+
body,
|
|
940
|
+
/* @__PURE__ */ jsx11("div", { className: "p-4", children: disconnect })
|
|
941
|
+
] }) });
|
|
942
|
+
}
|
|
943
|
+
function AccountDialogBody({ wallet, creating, setCreating }) {
|
|
944
|
+
const { auth } = useLensAuth();
|
|
945
|
+
const { completeSignIn } = useAccountDialogState();
|
|
946
|
+
const isDesktop = useMediaQuery("(min-width: 768px)");
|
|
947
|
+
const { data, loading } = useAccountsAvailable({ managedBy: evmAddress(wallet.address), includeOwned: true });
|
|
948
|
+
const accounts = data?.items;
|
|
949
|
+
const hasAccounts = !!accounts?.some((item) => item.account.username?.localName);
|
|
950
|
+
const showCreate = creating || !loading && !hasAccounts;
|
|
951
|
+
const signIn = async (account, challenge) => {
|
|
952
|
+
const signature = await auth.signChallenge(challenge, wallet);
|
|
953
|
+
await auth.authenticate(challenge, signature);
|
|
954
|
+
completeSignIn({ address: account.address.toLowerCase(), username: account.username?.localName ?? null });
|
|
955
|
+
};
|
|
956
|
+
const handleCreated = async (created) => {
|
|
957
|
+
completeSignIn({ address: created.address.toLowerCase(), username: created.localName });
|
|
958
|
+
};
|
|
959
|
+
const title = showCreate ? "Create Account" : "Select Account";
|
|
960
|
+
const description = showCreate ? hasAccounts ? "Pick a username for your new Lens account." : "You don\u2019t have a Lens account yet. Create one to continue." : "Select an account to continue.";
|
|
961
|
+
const Header = isDesktop ? DialogHeader : DrawerHeader;
|
|
962
|
+
const Title2 = isDesktop ? DialogTitle : DrawerTitle;
|
|
963
|
+
const Description2 = isDesktop ? DialogDescription : DrawerDescription;
|
|
964
|
+
return /* @__PURE__ */ jsxs5(Fragment, { children: [
|
|
965
|
+
/* @__PURE__ */ jsxs5(Header, { className: isDesktop ? void 0 : "text-left", children: [
|
|
966
|
+
/* @__PURE__ */ jsx11(Title2, { children: title }),
|
|
967
|
+
/* @__PURE__ */ jsx11(Description2, { children: description })
|
|
968
|
+
] }),
|
|
969
|
+
/* @__PURE__ */ jsx11("div", { className: isDesktop || !showCreate ? void 0 : "px-4", children: showCreate ? /* @__PURE__ */ jsx11(
|
|
970
|
+
CreateAccountForm,
|
|
971
|
+
{
|
|
972
|
+
ownerAddress: wallet.address,
|
|
973
|
+
onCreated: handleCreated,
|
|
974
|
+
onBack: hasAccounts ? () => setCreating(false) : void 0
|
|
975
|
+
}
|
|
976
|
+
) : /* @__PURE__ */ jsx11(AccountList, { auth, accounts, loading, ownerAddress: wallet.address, onSign: signIn, onCreate: () => setCreating(true) }) })
|
|
977
|
+
] });
|
|
978
|
+
}
|
|
979
|
+
function fetchChallenge(auth, acc, ownerAddress) {
|
|
980
|
+
return auth.requestChallenge({
|
|
981
|
+
role: acc.isOwner ? "ACCOUNT_OWNER" : "ACCOUNT_MANAGER",
|
|
982
|
+
account: acc.address,
|
|
983
|
+
signer: ownerAddress
|
|
984
|
+
});
|
|
985
|
+
}
|
|
986
|
+
function AccountList({ auth, accounts, loading, ownerAddress, onSign, onCreate }) {
|
|
987
|
+
const [step, setStep] = React3.useState(null);
|
|
988
|
+
const [errorId, setErrorId] = React3.useState(null);
|
|
989
|
+
const [challenges, setChallenges] = React3.useState({});
|
|
990
|
+
const selectable = React3.useMemo(() => (accounts ?? []).map((item, index) => {
|
|
991
|
+
const acc = { ...item.account, isOwner: item.__typename === "AccountOwned" };
|
|
992
|
+
return { acc, id: `${acc.address}-${index}` };
|
|
993
|
+
}), [accounts]);
|
|
994
|
+
const prefetch = React3.useCallback((acc, id) => {
|
|
995
|
+
fetchChallenge(auth, acc, ownerAddress).then((challenge) => setChallenges((prev) => ({ ...prev, [id]: { owner: ownerAddress, challenge, fetchedAt: Date.now() } }))).catch(() => {
|
|
996
|
+
});
|
|
997
|
+
}, [auth, ownerAddress]);
|
|
998
|
+
React3.useEffect(() => {
|
|
999
|
+
const refresh = () => {
|
|
1000
|
+
if (document.visibilityState !== "visible") return;
|
|
1001
|
+
for (const { acc, id } of selectable) {
|
|
1002
|
+
if (acc.username?.localName) prefetch(acc, id);
|
|
1003
|
+
}
|
|
1004
|
+
};
|
|
1005
|
+
refresh();
|
|
1006
|
+
const interval = setInterval(refresh, CHALLENGE_REFRESH_MS);
|
|
1007
|
+
document.addEventListener("visibilitychange", refresh);
|
|
1008
|
+
return () => {
|
|
1009
|
+
clearInterval(interval);
|
|
1010
|
+
document.removeEventListener("visibilitychange", refresh);
|
|
1011
|
+
};
|
|
1012
|
+
}, [selectable, prefetch]);
|
|
1013
|
+
const readyChallenge = (id) => {
|
|
1014
|
+
if (step?.id === id && step.status === "sign") return step.challenge;
|
|
1015
|
+
const prefetched = challenges[id];
|
|
1016
|
+
if (!prefetched || prefetched.owner !== ownerAddress) return null;
|
|
1017
|
+
return Date.now() - prefetched.fetchedAt < CHALLENGE_MAX_AGE_MS ? prefetched.challenge : null;
|
|
1018
|
+
};
|
|
1019
|
+
const handleClick = (acc, id) => {
|
|
1020
|
+
setErrorId(null);
|
|
1021
|
+
const fail = () => {
|
|
1022
|
+
setStep(null);
|
|
1023
|
+
setErrorId(id);
|
|
1024
|
+
prefetch(acc, id);
|
|
1025
|
+
};
|
|
1026
|
+
const challenge = readyChallenge(id);
|
|
1027
|
+
if (challenge) {
|
|
1028
|
+
const signing = onSign(acc, challenge);
|
|
1029
|
+
setChallenges(({ [id]: _used, ...rest }) => rest);
|
|
1030
|
+
setStep({ id, status: "loading" });
|
|
1031
|
+
signing.then(() => setStep(null), fail);
|
|
1032
|
+
return;
|
|
1033
|
+
}
|
|
1034
|
+
setStep({ id, status: "loading" });
|
|
1035
|
+
(async () => {
|
|
1036
|
+
const fetched = await fetchChallenge(auth, acc, ownerAddress);
|
|
1037
|
+
if (opensWalletWithDeepLink()) return setStep({ id, status: "sign", challenge: fetched });
|
|
1038
|
+
await onSign(acc, fetched);
|
|
1039
|
+
setStep(null);
|
|
1040
|
+
})().catch(fail);
|
|
1041
|
+
};
|
|
1042
|
+
if (loading) {
|
|
1043
|
+
return /* @__PURE__ */ jsx11("div", { className: "p-4", children: /* @__PURE__ */ jsx11("p", { className: "text-sm text-muted-foreground", children: "Loading available profiles\u2026" }) });
|
|
1044
|
+
}
|
|
1045
|
+
return /* @__PURE__ */ jsxs5("div", { className: "py-4 space-y-3", children: [
|
|
1046
|
+
/* @__PURE__ */ jsx11("ul", { className: "space-y-2 max-h-80 overflow-y-auto", children: selectable.map(({ acc, id }) => {
|
|
1047
|
+
const username = acc.username?.localName;
|
|
1048
|
+
if (!username) return null;
|
|
1049
|
+
const isLoading = step?.id === id && step.status === "loading";
|
|
1050
|
+
const awaitingSignature = step?.id === id && step.status === "sign";
|
|
1051
|
+
return /* @__PURE__ */ jsxs5("li", { children: [
|
|
1052
|
+
/* @__PURE__ */ jsxs5(
|
|
1053
|
+
"button",
|
|
1054
|
+
{
|
|
1055
|
+
type: "button",
|
|
1056
|
+
className: "w-full text-left px-3 py-2 rounded-md border hover:bg-accent/50 flex items-center justify-between cursor-pointer disabled:cursor-default",
|
|
1057
|
+
onClick: () => handleClick(acc, id),
|
|
1058
|
+
disabled: step?.status === "loading",
|
|
1059
|
+
children: [
|
|
1060
|
+
/* @__PURE__ */ jsx11("span", { className: "truncate", children: username }),
|
|
1061
|
+
isLoading && /* @__PURE__ */ jsx11(Loader22, { className: "size-4 animate-spin text-muted-foreground" }),
|
|
1062
|
+
awaitingSignature && /* @__PURE__ */ jsx11("span", { className: "text-sm font-medium text-primary", children: "Sign in" })
|
|
1063
|
+
]
|
|
1064
|
+
}
|
|
1065
|
+
),
|
|
1066
|
+
awaitingSignature && /* @__PURE__ */ jsx11("p", { className: "px-1 pt-1 text-xs text-muted-foreground", children: "Tap again to sign the message in your wallet." }),
|
|
1067
|
+
errorId === id && /* @__PURE__ */ jsx11("p", { className: "px-1 pt-1 text-xs text-destructive", children: "Couldn\u2019t sign in. Please try again." })
|
|
1068
|
+
] }, id);
|
|
1069
|
+
}) }),
|
|
1070
|
+
/* @__PURE__ */ jsx11(Button, { variant: "ghost", className: "w-full", onClick: onCreate, disabled: step?.status === "loading", children: "Create a new account" })
|
|
1071
|
+
] });
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
// src/react/provider.tsx
|
|
1075
|
+
import { jsx as jsx12, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1076
|
+
var LensAuthContext = React4.createContext(null);
|
|
1077
|
+
var AccountDialogContext = React4.createContext(null);
|
|
1078
|
+
function useLensAuth() {
|
|
1079
|
+
const ctx = React4.useContext(LensAuthContext);
|
|
1080
|
+
if (!ctx) throw new Error("useLensAuth must be used within <LensAuthProvider>");
|
|
1081
|
+
return ctx;
|
|
1082
|
+
}
|
|
1083
|
+
function useAccountDialogState() {
|
|
1084
|
+
const ctx = React4.useContext(AccountDialogContext);
|
|
1085
|
+
if (!ctx) throw new Error("<AccountDialog> must be used within <LensAuthProvider>");
|
|
1086
|
+
return ctx;
|
|
1087
|
+
}
|
|
1088
|
+
function subscribeToTokens(onChange) {
|
|
1089
|
+
window.addEventListener(TOKENS_CHANGED_EVENT, onChange);
|
|
1090
|
+
window.addEventListener("storage", onChange);
|
|
1091
|
+
return () => {
|
|
1092
|
+
window.removeEventListener(TOKENS_CHANGED_EVENT, onChange);
|
|
1093
|
+
window.removeEventListener("storage", onChange);
|
|
1094
|
+
};
|
|
1095
|
+
}
|
|
1096
|
+
function useSessionKey(auth) {
|
|
1097
|
+
return React4.useSyncExternalStore(
|
|
1098
|
+
subscribeToTokens,
|
|
1099
|
+
() => {
|
|
1100
|
+
const session = auth.getSession();
|
|
1101
|
+
return session ? `${session.sessionId}:${session.account}` : null;
|
|
1102
|
+
},
|
|
1103
|
+
() => void 0
|
|
1104
|
+
);
|
|
1105
|
+
}
|
|
1106
|
+
function createUseConvexAuth(auth) {
|
|
1107
|
+
return function useLensConvexAuth() {
|
|
1108
|
+
const sessionKey = useSessionKey(auth);
|
|
1109
|
+
const fetchAccessToken = React4.useCallback(
|
|
1110
|
+
async ({ forceRefreshToken }) => sessionKey ? auth.fetchConvexToken(forceRefreshToken) : null,
|
|
1111
|
+
[sessionKey]
|
|
1112
|
+
);
|
|
1113
|
+
return { isLoading: sessionKey === void 0, isAuthenticated: !!sessionKey, fetchAccessToken };
|
|
1114
|
+
};
|
|
1115
|
+
}
|
|
1116
|
+
function LensAuthProvider({ auth, convex, onSignIn, accountDialog = true, children }) {
|
|
1117
|
+
const [useConvexAuthHook] = React4.useState(() => createUseConvexAuth(auth));
|
|
1118
|
+
const content = /* @__PURE__ */ jsxs6(LensAuthState, { auth, onSignIn, withConvex: !!convex, children: [
|
|
1119
|
+
children,
|
|
1120
|
+
accountDialog && /* @__PURE__ */ jsx12(AccountDialog, {})
|
|
1121
|
+
] });
|
|
1122
|
+
return /* @__PURE__ */ jsx12(ThirdwebProvider, { children: /* @__PURE__ */ jsx12(LensProvider, { client: auth.lensClient, children: convex ? /* @__PURE__ */ jsx12(ConvexProviderWithAuth, { client: convex, useAuth: useConvexAuthHook, children: content }) : content }) });
|
|
1123
|
+
}
|
|
1124
|
+
function readSelectedAccount(auth, address) {
|
|
1125
|
+
try {
|
|
1126
|
+
const stored = JSON.parse(localStorage.getItem(`${auth.storagePrefix}account`) ?? "null");
|
|
1127
|
+
return stored?.address === address && typeof stored.username === "string" ? stored.username : null;
|
|
1128
|
+
} catch {
|
|
1129
|
+
return null;
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
function writeSelectedAccount(auth, account) {
|
|
1133
|
+
try {
|
|
1134
|
+
const key = `${auth.storagePrefix}account`;
|
|
1135
|
+
if (account) localStorage.setItem(key, JSON.stringify(account));
|
|
1136
|
+
else localStorage.removeItem(key);
|
|
1137
|
+
} catch {
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
function LensAuthState({ auth, onSignIn, withConvex, children }) {
|
|
1141
|
+
const wallet = useActiveAccount2();
|
|
1142
|
+
const activeWallet = useActiveWallet2();
|
|
1143
|
+
const { disconnect } = useDisconnect();
|
|
1144
|
+
const sessionKey = useSessionKey(auth);
|
|
1145
|
+
const [manualOpen, setManualOpen] = React4.useState(false);
|
|
1146
|
+
const [pendingSignIn, setPendingSignIn] = React4.useState(null);
|
|
1147
|
+
const account = React4.useMemo(() => {
|
|
1148
|
+
if (!sessionKey) return null;
|
|
1149
|
+
const address = sessionKey.slice(sessionKey.lastIndexOf(":") + 1);
|
|
1150
|
+
return { address, username: readSelectedAccount(auth, address) };
|
|
1151
|
+
}, [auth, sessionKey]);
|
|
1152
|
+
const status = sessionKey === void 0 ? "loading" : sessionKey ? "signed-in" : wallet ? "choosing-account" : "signed-out";
|
|
1153
|
+
useBackgroundRefresh(auth);
|
|
1154
|
+
React4.useEffect(() => {
|
|
1155
|
+
if (wallet) void auth.resumeSession().catch(() => {
|
|
1156
|
+
});
|
|
1157
|
+
}, [auth, wallet?.address]);
|
|
1158
|
+
const signOut = React4.useCallback(async () => {
|
|
1159
|
+
setManualOpen(false);
|
|
1160
|
+
writeSelectedAccount(auth, null);
|
|
1161
|
+
try {
|
|
1162
|
+
await auth.logout();
|
|
1163
|
+
} finally {
|
|
1164
|
+
if (activeWallet) disconnect(activeWallet);
|
|
1165
|
+
}
|
|
1166
|
+
}, [auth, activeWallet, disconnect]);
|
|
1167
|
+
const value = React4.useMemo(() => ({
|
|
1168
|
+
auth,
|
|
1169
|
+
status,
|
|
1170
|
+
isLoading: status === "loading",
|
|
1171
|
+
isSignedIn: status === "signed-in",
|
|
1172
|
+
wallet,
|
|
1173
|
+
account,
|
|
1174
|
+
signOut,
|
|
1175
|
+
openAccountDialog: () => setManualOpen(true)
|
|
1176
|
+
}), [auth, status, wallet, account, signOut]);
|
|
1177
|
+
const required = status === "choosing-account";
|
|
1178
|
+
const dialog = React4.useMemo(() => ({
|
|
1179
|
+
open: required || manualOpen && !!wallet,
|
|
1180
|
+
required,
|
|
1181
|
+
setOpen: (open) => {
|
|
1182
|
+
if (open) setManualOpen(true);
|
|
1183
|
+
else if (!required) setManualOpen(false);
|
|
1184
|
+
},
|
|
1185
|
+
completeSignIn: (signedIn) => {
|
|
1186
|
+
writeSelectedAccount(auth, signedIn);
|
|
1187
|
+
setManualOpen(false);
|
|
1188
|
+
if (onSignIn) setPendingSignIn(signedIn);
|
|
1189
|
+
}
|
|
1190
|
+
}), [auth, required, manualOpen, wallet, onSignIn]);
|
|
1191
|
+
return /* @__PURE__ */ jsx12(LensAuthContext.Provider, { value, children: /* @__PURE__ */ jsxs6(AccountDialogContext.Provider, { value: dialog, children: [
|
|
1192
|
+
children,
|
|
1193
|
+
pendingSignIn && onSignIn && (withConvex ? /* @__PURE__ */ jsx12(ConvexSignInCallback, { account: pendingSignIn, onSignIn, done: () => setPendingSignIn(null) }) : /* @__PURE__ */ jsx12(SignInCallback, { account: pendingSignIn, onSignIn, done: () => setPendingSignIn(null) }))
|
|
1194
|
+
] }) });
|
|
1195
|
+
}
|
|
1196
|
+
function SignInCallback({ account, onSignIn, done }) {
|
|
1197
|
+
React4.useEffect(() => {
|
|
1198
|
+
Promise.resolve(onSignIn(account)).catch((err) => console.error("lens-convex-auth: onSignIn failed", err)).finally(done);
|
|
1199
|
+
}, [account]);
|
|
1200
|
+
return null;
|
|
1201
|
+
}
|
|
1202
|
+
function ConvexSignInCallback(props) {
|
|
1203
|
+
const { isAuthenticated, isLoading } = useConvexAuth();
|
|
1204
|
+
return isAuthenticated && !isLoading ? /* @__PURE__ */ jsx12(SignInCallback, { ...props }) : null;
|
|
1205
|
+
}
|
|
1206
|
+
function useBackgroundRefresh(auth) {
|
|
1207
|
+
React4.useEffect(() => {
|
|
1208
|
+
let timeout;
|
|
1209
|
+
let stopped = false;
|
|
1210
|
+
const schedule = () => {
|
|
1211
|
+
if (timeout) clearTimeout(timeout);
|
|
1212
|
+
timeout = void 0;
|
|
1213
|
+
const tokens = auth.tokens.getStoredTokens();
|
|
1214
|
+
if (stopped || !tokens?.expiresAt) return;
|
|
1215
|
+
const delay = Math.max(1e3, tokens.expiresAt - Date.now() - 60 * 1e3);
|
|
1216
|
+
timeout = setTimeout(() => void auth.tokens.refreshTokens(), delay);
|
|
1217
|
+
};
|
|
1218
|
+
const unsubscribe = subscribeToTokens(schedule);
|
|
1219
|
+
(async () => {
|
|
1220
|
+
try {
|
|
1221
|
+
await auth.resumeSession();
|
|
1222
|
+
} catch {
|
|
1223
|
+
}
|
|
1224
|
+
schedule();
|
|
1225
|
+
})();
|
|
1226
|
+
return () => {
|
|
1227
|
+
stopped = true;
|
|
1228
|
+
unsubscribe();
|
|
1229
|
+
if (timeout) clearTimeout(timeout);
|
|
1230
|
+
};
|
|
1231
|
+
}, [auth]);
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
// src/react/SignInButton.tsx
|
|
1235
|
+
import * as React5 from "react";
|
|
1236
|
+
import { jsx as jsx13 } from "react/jsx-runtime";
|
|
1237
|
+
var ConnectButton = React5.lazy(() => import("thirdweb/react").then((m) => ({ default: m.ConnectButton })));
|
|
1238
|
+
function SignInButton({ label = "Sign in", signOutLabel = "Logout", theme, className, connectButtonProps }) {
|
|
1239
|
+
const { auth, wallet, status, signOut } = useLensAuth();
|
|
1240
|
+
const documentTheme = useDocumentTheme();
|
|
1241
|
+
const [mounted, setMounted] = React5.useState(false);
|
|
1242
|
+
React5.useEffect(() => setMounted(true), []);
|
|
1243
|
+
const placeholder = /* @__PURE__ */ jsx13(Button, { variant: "outline", size: "xl", className: cn("min-w-[165px] px-0", className), disabled: true, children: "Loading..." });
|
|
1244
|
+
if (!mounted) return placeholder;
|
|
1245
|
+
if (!wallet) {
|
|
1246
|
+
return /* @__PURE__ */ jsx13(React5.Suspense, { fallback: placeholder, children: /* @__PURE__ */ jsx13(
|
|
1247
|
+
ConnectButton,
|
|
1248
|
+
{
|
|
1249
|
+
client: auth.thirdwebClient,
|
|
1250
|
+
chain: auth.chain,
|
|
1251
|
+
theme: theme ?? documentTheme,
|
|
1252
|
+
...connectButtonProps,
|
|
1253
|
+
connectButton: {
|
|
1254
|
+
label,
|
|
1255
|
+
...connectButtonProps?.connectButton,
|
|
1256
|
+
style: { height: "50px", minWidth: "165px", ...connectButtonProps?.connectButton?.style }
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
) });
|
|
1260
|
+
}
|
|
1261
|
+
if (status !== "signed-in") return placeholder;
|
|
1262
|
+
return /* @__PURE__ */ jsx13(Button, { variant: "ghost", size: "xl", className: cn("min-w-[165px] px-0", className), onClick: signOut, children: signOutLabel });
|
|
1263
|
+
}
|
|
1264
|
+
export {
|
|
1265
|
+
AccountDialog,
|
|
1266
|
+
CreateAccountForm,
|
|
1267
|
+
LensAuthProvider,
|
|
1268
|
+
SignInButton,
|
|
1269
|
+
useLensAuth
|
|
1270
|
+
};
|
|
1271
|
+
//# sourceMappingURL=index.js.map
|