@better-auth-ui/heroui 1.6.2 → 1.6.4
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/dist/components/auth/additional-field.d.ts +4 -0
- package/dist/components/auth/additional-field.js +270 -0
- package/dist/components/auth/auth-provider.d.ts +4 -6
- package/dist/components/auth/auth-provider.js +12 -11
- package/dist/components/auth/auth.d.ts +1 -1
- package/dist/components/auth/auth.js +54 -28
- package/dist/components/{settings/security → auth/delete-user}/danger-zone.d.ts +3 -4
- package/dist/components/{settings/security → auth/delete-user}/danger-zone.js +1 -1
- package/dist/components/{settings/security → auth/delete-user}/delete-user.d.ts +1 -1
- package/dist/components/auth/delete-user/delete-user.js +95 -0
- package/dist/components/{email → auth/email}/email-changed-email.d.ts +2 -2
- package/dist/components/{email → auth/email}/email-changed-email.js +7 -7
- package/dist/components/{email → auth/email}/email-verification-email.d.ts +2 -2
- package/dist/components/{email → auth/email}/email-verification-email.js +7 -7
- package/dist/components/auth/email/magic-link-email.d.ts +3 -0
- package/dist/components/{email → auth/email}/magic-link-email.js +7 -7
- package/dist/components/auth/email/new-device-email.d.ts +3 -0
- package/dist/components/{email → auth/email}/new-device-email.js +7 -7
- package/dist/components/auth/email/otp-email.d.ts +3 -0
- package/dist/components/{email → auth/email}/otp-email.js +6 -6
- package/dist/components/{email → auth/email}/password-changed-email.d.ts +2 -2
- package/dist/components/{email → auth/email}/password-changed-email.js +7 -7
- package/dist/components/{email → auth/email}/reset-password-email.d.ts +2 -2
- package/dist/components/{email → auth/email}/reset-password-email.js +7 -7
- package/dist/components/auth/error-toaster.d.ts +1 -0
- package/dist/components/auth/error-toaster.js +17 -0
- package/dist/components/auth/forgot-password.d.ts +2 -2
- package/dist/components/auth/forgot-password.js +42 -42
- package/dist/components/auth/magic-link/magic-link-button.d.ts +11 -0
- package/dist/components/auth/magic-link/magic-link-button.js +20 -0
- package/dist/components/auth/magic-link/magic-link.d.ts +16 -0
- package/dist/components/auth/magic-link/magic-link.js +87 -0
- package/dist/components/{settings/account → auth/multi-session}/manage-account.d.ts +2 -6
- package/dist/components/auth/multi-session/manage-account.js +49 -0
- package/dist/components/{settings/account → auth/multi-session}/manage-accounts.d.ts +1 -1
- package/dist/components/auth/multi-session/manage-accounts.js +34 -0
- package/dist/components/{user/switch-account-menu.d.ts → auth/multi-session/switch-account-submenu-content.d.ts} +3 -3
- package/dist/components/auth/multi-session/switch-account-submenu-content.js +29 -0
- package/dist/components/{user/switch-account-item.d.ts → auth/multi-session/switch-account-submenu-item.d.ts} +2 -2
- package/dist/components/auth/multi-session/switch-account-submenu-item.js +20 -0
- package/dist/components/auth/multi-session/switch-account-submenu.d.ts +13 -0
- package/dist/components/auth/multi-session/switch-account-submenu.js +24 -0
- package/dist/components/auth/passkey/passkey-button.d.ts +6 -0
- package/dist/components/auth/passkey/passkey-button.js +24 -0
- package/dist/components/auth/passkey/passkey.js +45 -0
- package/dist/components/{settings/security → auth/passkey}/passkeys.d.ts +1 -1
- package/dist/components/auth/passkey/passkeys.js +106 -0
- package/dist/components/auth/provider-button.d.ts +13 -0
- package/dist/components/auth/provider-button.js +25 -0
- package/dist/components/auth/provider-buttons.d.ts +3 -10
- package/dist/components/auth/provider-buttons.js +14 -22
- package/dist/components/auth/reset-password.d.ts +1 -1
- package/dist/components/auth/reset-password.js +110 -110
- package/dist/components/{settings → auth/settings}/account/account-settings.d.ts +5 -2
- package/dist/components/auth/settings/account/account-settings.js +20 -0
- package/dist/components/auth/settings/account/change-avatar.js +89 -0
- package/dist/components/{settings → auth/settings}/account/change-email.d.ts +1 -1
- package/dist/components/{settings → auth/settings}/account/change-email.js +17 -20
- package/dist/components/{settings → auth/settings}/account/user-profile.d.ts +3 -4
- package/dist/components/auth/settings/account/user-profile.js +86 -0
- package/dist/components/auth/settings/security/active-session.js +59 -0
- package/dist/components/{settings → auth/settings}/security/active-sessions.d.ts +1 -1
- package/dist/components/auth/settings/security/active-sessions.js +34 -0
- package/dist/components/{settings → auth/settings}/security/change-password.d.ts +1 -1
- package/dist/components/auth/settings/security/change-password.js +189 -0
- package/dist/components/auth/settings/security/linked-account.js +56 -0
- package/dist/components/{settings → auth/settings}/security/linked-accounts.d.ts +1 -1
- package/dist/components/auth/settings/security/linked-accounts.js +45 -0
- package/dist/components/{settings → auth/settings}/security/security-settings.d.ts +1 -1
- package/dist/components/auth/settings/security/security-settings.js +22 -0
- package/dist/components/auth/settings/settings.js +45 -0
- package/dist/components/auth/sign-in.d.ts +1 -1
- package/dist/components/auth/sign-in.js +64 -100
- package/dist/components/auth/sign-out.js +11 -11
- package/dist/components/auth/sign-up.d.ts +1 -1
- package/dist/components/auth/sign-up.js +123 -157
- package/dist/components/{settings/account → auth/theme}/appearance.d.ts +3 -6
- package/dist/components/auth/theme/appearance.js +72 -0
- package/dist/components/auth/theme/theme-toggle-item.d.ts +5 -0
- package/dist/components/auth/theme/theme-toggle-item.js +58 -0
- package/dist/components/auth/user/user-avatar.js +29 -0
- package/dist/components/{user → auth/user}/user-button.d.ts +2 -6
- package/dist/components/auth/user/user-button.js +55 -0
- package/dist/components/auth/user/user-view.js +37 -0
- package/dist/components/auth/username/sign-in-username.d.ts +14 -0
- package/dist/components/auth/username/sign-in-username.js +142 -0
- package/dist/components/auth/username/username-field.d.ts +8 -0
- package/dist/components/auth/username/username-field.js +51 -0
- package/dist/email.d.ts +7 -0
- package/dist/email.js +9 -0
- package/dist/index.d.ts +13 -27
- package/dist/index.js +22 -36
- package/dist/lib/auth/auth-plugin.d.ts +44 -0
- package/dist/lib/auth/delete-user-plugin.d.ts +17 -0
- package/dist/lib/auth/delete-user-plugin.js +10 -0
- package/dist/lib/auth/magic-link-plugin.d.ts +31 -0
- package/dist/lib/auth/magic-link-plugin.js +13 -0
- package/dist/lib/auth/multi-session-plugin.d.ts +18 -0
- package/dist/lib/auth/multi-session-plugin.js +12 -0
- package/dist/lib/auth/passkey-plugin.d.ts +20 -0
- package/dist/lib/auth/passkey-plugin.js +12 -0
- package/dist/lib/auth/theme-plugin.d.ts +68 -0
- package/dist/lib/auth/theme-plugin.js +23 -0
- package/dist/lib/auth/username-plugin.d.ts +63 -0
- package/dist/lib/auth/username-plugin.js +18 -0
- package/dist/plugins.d.ts +20 -0
- package/dist/plugins.js +21 -0
- package/package.json +22 -22
- package/src/components/auth/additional-field.tsx +554 -0
- package/src/components/auth/auth-provider.tsx +8 -5
- package/src/components/auth/auth.tsx +100 -36
- package/src/components/{settings/security → auth/delete-user}/danger-zone.tsx +3 -10
- package/src/components/{settings/security → auth/delete-user}/delete-user.tsx +31 -24
- package/src/components/{email → auth/email}/email-changed-email.tsx +2 -2
- package/src/components/{email → auth/email}/email-verification-email.tsx +2 -2
- package/src/components/{email → auth/email}/magic-link-email.tsx +2 -2
- package/src/components/{email → auth/email}/new-device-email.tsx +2 -2
- package/src/components/{email → auth/email}/otp-email.tsx +2 -2
- package/src/components/{email → auth/email}/password-changed-email.tsx +2 -2
- package/src/components/{email → auth/email}/reset-password-email.tsx +2 -2
- package/src/components/auth/error-toaster.tsx +25 -0
- package/src/components/auth/forgot-password.tsx +15 -13
- package/src/components/auth/magic-link/magic-link-button.tsx +61 -0
- package/src/components/auth/{magic-link.tsx → magic-link/magic-link.tsx} +52 -57
- package/src/components/{settings/account → auth/multi-session}/manage-account.tsx +15 -15
- package/src/components/{settings/account → auth/multi-session}/manage-accounts.tsx +16 -12
- package/src/components/{user/switch-account-menu.tsx → auth/multi-session/switch-account-submenu-content.tsx} +19 -17
- package/src/components/{user/switch-account-item.tsx → auth/multi-session/switch-account-submenu-item.tsx} +18 -8
- package/src/components/auth/multi-session/switch-account-submenu.tsx +50 -0
- package/src/components/auth/passkey/passkey-button.tsx +56 -0
- package/src/components/{settings/security → auth/passkey}/passkey.tsx +21 -9
- package/src/components/auth/passkey/passkeys.tsx +196 -0
- package/src/components/auth/provider-button.tsx +65 -0
- package/src/components/auth/provider-buttons.tsx +23 -45
- package/src/components/auth/reset-password.tsx +14 -9
- package/src/components/{settings → auth/settings}/account/account-settings.tsx +15 -13
- package/src/components/{settings → auth/settings}/account/change-avatar.tsx +4 -5
- package/src/components/{settings → auth/settings}/account/change-email.tsx +5 -6
- package/src/components/auth/settings/account/user-profile.tsx +171 -0
- package/src/components/{settings → auth/settings}/security/active-session.tsx +8 -6
- package/src/components/{settings → auth/settings}/security/active-sessions.tsx +5 -10
- package/src/components/{settings → auth/settings}/security/change-password.tsx +22 -14
- package/src/components/{settings → auth/settings}/security/linked-account.tsx +10 -16
- package/src/components/{settings → auth/settings}/security/linked-accounts.tsx +4 -9
- package/src/components/{settings → auth/settings}/security/security-settings.tsx +7 -6
- package/src/components/{settings → auth/settings}/settings.tsx +2 -2
- package/src/components/auth/sign-in.tsx +39 -80
- package/src/components/auth/sign-out.tsx +2 -2
- package/src/components/auth/sign-up.tsx +110 -147
- package/src/components/{settings/account → auth/theme}/appearance.tsx +18 -25
- package/src/components/auth/theme/theme-toggle-item.tsx +110 -0
- package/src/components/{user → auth/user}/user-avatar.tsx +12 -4
- package/src/components/{user → auth/user}/user-button.tsx +15 -88
- package/src/components/{user → auth/user}/user-view.tsx +12 -4
- package/src/components/auth/username/sign-in-username.tsx +283 -0
- package/src/components/auth/username/username-field.tsx +122 -0
- package/src/email.ts +9 -0
- package/src/index.tsx +13 -27
- package/src/lib/auth/auth-plugin.ts +67 -0
- package/src/lib/auth/delete-user-plugin.ts +15 -0
- package/src/lib/auth/magic-link-plugin.ts +26 -0
- package/src/lib/auth/multi-session-plugin.ts +17 -0
- package/src/lib/auth/passkey-plugin.ts +17 -0
- package/src/lib/auth/theme-plugin.ts +86 -0
- package/src/lib/auth/username-plugin.tsx +30 -0
- package/src/plugins.ts +23 -0
- package/dist/components/auth/magic-link-button.d.ts +0 -13
- package/dist/components/auth/magic-link-button.js +0 -16
- package/dist/components/auth/magic-link.d.ts +0 -17
- package/dist/components/auth/magic-link.js +0 -111
- package/dist/components/auth/passkey-button.d.ts +0 -4
- package/dist/components/auth/passkey-button.js +0 -24
- package/dist/components/email/magic-link-email.d.ts +0 -3
- package/dist/components/email/new-device-email.d.ts +0 -3
- package/dist/components/email/otp-email.d.ts +0 -3
- package/dist/components/settings/account/account-settings.js +0 -23
- package/dist/components/settings/account/appearance.js +0 -71
- package/dist/components/settings/account/change-avatar.js +0 -89
- package/dist/components/settings/account/manage-account.js +0 -51
- package/dist/components/settings/account/manage-accounts.js +0 -33
- package/dist/components/settings/account/user-profile.js +0 -106
- package/dist/components/settings/security/active-session.js +0 -62
- package/dist/components/settings/security/active-sessions.js +0 -34
- package/dist/components/settings/security/change-password.js +0 -192
- package/dist/components/settings/security/delete-user.js +0 -97
- package/dist/components/settings/security/linked-account.js +0 -62
- package/dist/components/settings/security/linked-accounts.js +0 -45
- package/dist/components/settings/security/passkey.js +0 -43
- package/dist/components/settings/security/passkeys.js +0 -53
- package/dist/components/settings/security/security-settings.js +0 -25
- package/dist/components/settings/settings.js +0 -45
- package/dist/components/user/switch-account-item.js +0 -20
- package/dist/components/user/switch-account-menu.js +0 -28
- package/dist/components/user/user-avatar.js +0 -29
- package/dist/components/user/user-button.js +0 -91
- package/dist/components/user/user-view.js +0 -37
- package/dist/core.d.ts +0 -1
- package/dist/core.js +0 -1
- package/dist/react-exports.d.ts +0 -1
- package/dist/react-exports.js +0 -1
- package/src/components/auth/magic-link-button.tsx +0 -43
- package/src/components/auth/passkey-button.tsx +0 -36
- package/src/components/settings/account/user-profile.tsx +0 -218
- package/src/components/settings/security/passkeys.tsx +0 -104
- package/src/core.ts +0 -1
- package/src/react-exports.ts +0 -1
- /package/dist/components/{settings/security → auth/passkey}/passkey.d.ts +0 -0
- /package/dist/components/{settings → auth/settings}/account/change-avatar.d.ts +0 -0
- /package/dist/components/{settings → auth/settings}/security/active-session.d.ts +0 -0
- /package/dist/components/{settings → auth/settings}/security/linked-account.d.ts +0 -0
- /package/dist/components/{settings → auth/settings}/settings.d.ts +0 -0
- /package/dist/components/{user → auth/user}/user-avatar.d.ts +0 -0
- /package/dist/components/{user → auth/user}/user-view.d.ts +0 -0
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { authMutationKeys } from "@better-auth-ui/core"
|
|
1
2
|
import {
|
|
3
|
+
type MagicLinkAuthClient,
|
|
2
4
|
useAuth,
|
|
3
|
-
|
|
4
|
-
|
|
5
|
+
useAuthPlugin,
|
|
6
|
+
useSignInMagicLink
|
|
5
7
|
} from "@better-auth-ui/react"
|
|
6
8
|
import {
|
|
7
9
|
Button,
|
|
@@ -18,12 +20,12 @@ import {
|
|
|
18
20
|
TextField,
|
|
19
21
|
toast
|
|
20
22
|
} from "@heroui/react"
|
|
23
|
+
import { useIsMutating } from "@tanstack/react-query"
|
|
21
24
|
import { type SyntheticEvent, useState } from "react"
|
|
22
25
|
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
26
|
-
import { ProviderButtons, type SocialLayout } from "./provider-buttons"
|
|
26
|
+
import { magicLinkPlugin } from "../../../lib/auth/magic-link-plugin"
|
|
27
|
+
import { FieldSeparator } from "../field-separator"
|
|
28
|
+
import { ProviderButtons, type SocialLayout } from "../provider-buttons"
|
|
27
29
|
|
|
28
30
|
export type MagicLinkProps = {
|
|
29
31
|
className?: string
|
|
@@ -33,12 +35,11 @@ export type MagicLinkProps = {
|
|
|
33
35
|
}
|
|
34
36
|
|
|
35
37
|
/**
|
|
36
|
-
*
|
|
38
|
+
* Magic-link sign-in form.
|
|
37
39
|
*
|
|
38
|
-
* @param
|
|
39
|
-
* @param
|
|
40
|
-
* @param
|
|
41
|
-
* @returns The magic-link sign-in UI as a JSX element
|
|
40
|
+
* @param socialLayout - Provider button layout.
|
|
41
|
+
* @param socialPosition - `"top"` or `"bottom"`. Defaults to `"bottom"`.
|
|
42
|
+
* @param variant - Card variant.
|
|
42
43
|
*/
|
|
43
44
|
export function MagicLink({
|
|
44
45
|
className,
|
|
@@ -46,42 +47,37 @@ export function MagicLink({
|
|
|
46
47
|
socialPosition = "bottom",
|
|
47
48
|
variant,
|
|
48
49
|
...props
|
|
49
|
-
}: MagicLinkProps & CardProps) {
|
|
50
|
+
}: MagicLinkProps & Omit<CardProps, "children">) {
|
|
50
51
|
const {
|
|
52
|
+
authClient,
|
|
51
53
|
basePaths,
|
|
52
54
|
baseURL,
|
|
55
|
+
emailAndPassword,
|
|
53
56
|
localization,
|
|
54
|
-
|
|
57
|
+
plugins,
|
|
55
58
|
redirectTo,
|
|
56
59
|
socialProviders,
|
|
57
60
|
viewPaths
|
|
58
61
|
} = useAuth()
|
|
62
|
+
const { localization: magicLinkLocalization } = useAuthPlugin(magicLinkPlugin)
|
|
59
63
|
|
|
60
64
|
const [email, setEmail] = useState("")
|
|
61
65
|
|
|
62
|
-
const { mutate: signInMagicLink, isPending:
|
|
63
|
-
useSignInMagicLink({
|
|
64
|
-
onError: (error) => toast.danger(error.error?.message || error.message),
|
|
66
|
+
const { mutate: signInMagicLink, isPending: signInMagicLinkPending } =
|
|
67
|
+
useSignInMagicLink(authClient as MagicLinkAuthClient, {
|
|
65
68
|
onSuccess: () => {
|
|
66
69
|
setEmail("")
|
|
67
|
-
toast.success(
|
|
70
|
+
toast.success(magicLinkLocalization.magicLinkSent)
|
|
68
71
|
}
|
|
69
72
|
})
|
|
70
73
|
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
const { mutate: signInSocial, isPending: socialPending } = useSignInSocial({
|
|
74
|
-
onError: (error) => toast.danger(error.error?.message || error.message),
|
|
75
|
-
onSuccess: () => {
|
|
76
|
-
setSocialRedirecting(true)
|
|
77
|
-
|
|
78
|
-
setTimeout(() => {
|
|
79
|
-
setSocialRedirecting(false)
|
|
80
|
-
}, 5000)
|
|
81
|
-
}
|
|
74
|
+
const signInMutating = useIsMutating({
|
|
75
|
+
mutationKey: authMutationKeys.signIn.all
|
|
82
76
|
})
|
|
83
|
-
|
|
84
|
-
|
|
77
|
+
const signUpMutating = useIsMutating({
|
|
78
|
+
mutationKey: authMutationKeys.signUp.all
|
|
79
|
+
})
|
|
80
|
+
const isPending = signInMutating + signUpMutating > 0
|
|
85
81
|
|
|
86
82
|
const handleSubmit = (e: SyntheticEvent<HTMLFormElement>) => {
|
|
87
83
|
e.preventDefault()
|
|
@@ -92,7 +88,7 @@ export function MagicLink({
|
|
|
92
88
|
|
|
93
89
|
return (
|
|
94
90
|
<Card
|
|
95
|
-
className={cn("w-full max-w-sm
|
|
91
|
+
className={cn("w-full max-w-sm gap-4 md:p-6", className)}
|
|
96
92
|
variant={variant}
|
|
97
93
|
{...props}
|
|
98
94
|
>
|
|
@@ -106,11 +102,7 @@ export function MagicLink({
|
|
|
106
102
|
{socialPosition === "top" && (
|
|
107
103
|
<>
|
|
108
104
|
{!!socialProviders?.length && (
|
|
109
|
-
<ProviderButtons
|
|
110
|
-
socialLayout={socialLayout}
|
|
111
|
-
signInSocial={signInSocial}
|
|
112
|
-
isPending={isPending}
|
|
113
|
-
/>
|
|
105
|
+
<ProviderButtons socialLayout={socialLayout} />
|
|
114
106
|
)}
|
|
115
107
|
|
|
116
108
|
{showSeparator && (
|
|
@@ -141,14 +133,19 @@ export function MagicLink({
|
|
|
141
133
|
|
|
142
134
|
<div className="flex flex-col gap-3">
|
|
143
135
|
<Button type="submit" className="w-full" isPending={isPending}>
|
|
144
|
-
{
|
|
136
|
+
{signInMagicLinkPending && <Spinner color="current" size="sm" />}
|
|
145
137
|
|
|
146
|
-
{
|
|
138
|
+
{magicLinkLocalization.sendMagicLink}
|
|
147
139
|
</Button>
|
|
148
140
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
141
|
+
{plugins.flatMap((plugin) =>
|
|
142
|
+
(plugin.authButtons ?? []).map((AuthButton, index) => (
|
|
143
|
+
<AuthButton
|
|
144
|
+
key={`${plugin.id}-${index.toString()}`}
|
|
145
|
+
view="magicLink"
|
|
146
|
+
/>
|
|
147
|
+
))
|
|
148
|
+
)}
|
|
152
149
|
</div>
|
|
153
150
|
</Form>
|
|
154
151
|
|
|
@@ -159,27 +156,25 @@ export function MagicLink({
|
|
|
159
156
|
)}
|
|
160
157
|
|
|
161
158
|
{!!socialProviders?.length && (
|
|
162
|
-
<ProviderButtons
|
|
163
|
-
socialLayout={socialLayout}
|
|
164
|
-
signInSocial={signInSocial}
|
|
165
|
-
isPending={isPending}
|
|
166
|
-
/>
|
|
159
|
+
<ProviderButtons socialLayout={socialLayout} />
|
|
167
160
|
)}
|
|
168
161
|
</>
|
|
169
162
|
)}
|
|
170
163
|
</Card.Content>
|
|
171
164
|
|
|
172
|
-
|
|
173
|
-
<
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
165
|
+
{emailAndPassword?.enabled && (
|
|
166
|
+
<Card.Footer className="flex-col gap-3">
|
|
167
|
+
<Description className="text-sm">
|
|
168
|
+
{localization.auth.needToCreateAnAccount}{" "}
|
|
169
|
+
<Link
|
|
170
|
+
href={`${basePaths.auth}/${viewPaths.auth.signUp}`}
|
|
171
|
+
className="text-accent no-underline hover:underline decoration-accent-hover"
|
|
172
|
+
>
|
|
173
|
+
{localization.auth.signUp}
|
|
174
|
+
</Link>
|
|
175
|
+
</Description>
|
|
176
|
+
</Card.Footer>
|
|
177
|
+
)}
|
|
183
178
|
</Card>
|
|
184
179
|
)
|
|
185
180
|
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
|
+
type ListDeviceSession,
|
|
3
|
+
type MultiSessionAuthClient,
|
|
2
4
|
useAuth,
|
|
5
|
+
useAuthPlugin,
|
|
3
6
|
useRevokeMultiSession,
|
|
4
7
|
useSetActiveSession,
|
|
5
8
|
useUser
|
|
@@ -10,17 +13,13 @@ import {
|
|
|
10
13
|
Ellipsis
|
|
11
14
|
} from "@gravity-ui/icons"
|
|
12
15
|
import { Button, Dropdown, Spinner, toast } from "@heroui/react"
|
|
13
|
-
import type { Session, User } from "better-auth"
|
|
14
16
|
|
|
15
|
-
import {
|
|
17
|
+
import { multiSessionPlugin } from "../../../lib/auth/multi-session-plugin"
|
|
16
18
|
|
|
17
|
-
|
|
18
|
-
session: Session
|
|
19
|
-
user: User
|
|
20
|
-
}
|
|
19
|
+
import { UserView } from "../user/user-view"
|
|
21
20
|
|
|
22
21
|
export type ManageAccountProps = {
|
|
23
|
-
deviceSession?:
|
|
22
|
+
deviceSession?: ListDeviceSession | null
|
|
24
23
|
isPending?: boolean
|
|
25
24
|
}
|
|
26
25
|
|
|
@@ -37,17 +36,18 @@ export function ManageAccount({
|
|
|
37
36
|
deviceSession,
|
|
38
37
|
isPending
|
|
39
38
|
}: ManageAccountProps) {
|
|
40
|
-
const { localization } = useAuth()
|
|
41
|
-
const {
|
|
39
|
+
const { authClient, localization } = useAuth()
|
|
40
|
+
const { localization: multiSessionLocalization } =
|
|
41
|
+
useAuthPlugin(multiSessionPlugin)
|
|
42
|
+
const { data: user } = useUser(authClient)
|
|
42
43
|
|
|
43
44
|
const { mutate: setActiveSession, isPending: isSwitching } =
|
|
44
|
-
useSetActiveSession({
|
|
45
|
-
|
|
45
|
+
useSetActiveSession(authClient as MultiSessionAuthClient, {
|
|
46
|
+
onSuccess: () => window.scrollTo({ top: 0 })
|
|
46
47
|
})
|
|
47
48
|
|
|
48
49
|
const { mutate: revokeSession, isPending: isRevoking } =
|
|
49
|
-
useRevokeMultiSession({
|
|
50
|
-
onError: (error) => toast.danger(error.error?.message || error.message),
|
|
50
|
+
useRevokeMultiSession(authClient as MultiSessionAuthClient, {
|
|
51
51
|
onSuccess: () => toast.success(localization.settings.revokeSessionSuccess)
|
|
52
52
|
})
|
|
53
53
|
|
|
@@ -92,7 +92,7 @@ export function ManageAccount({
|
|
|
92
92
|
<Dropdown.Popover>
|
|
93
93
|
<Dropdown.Menu>
|
|
94
94
|
<Dropdown.Item
|
|
95
|
-
textValue={
|
|
95
|
+
textValue={multiSessionLocalization.switchAccount}
|
|
96
96
|
onAction={() =>
|
|
97
97
|
setActiveSession({
|
|
98
98
|
sessionToken: deviceSession.session.token
|
|
@@ -100,7 +100,7 @@ export function ManageAccount({
|
|
|
100
100
|
}
|
|
101
101
|
>
|
|
102
102
|
<ArrowRightArrowLeft className="text-muted" />
|
|
103
|
-
{
|
|
103
|
+
{multiSessionLocalization.switchAccount}
|
|
104
104
|
</Dropdown.Item>
|
|
105
105
|
|
|
106
106
|
<Dropdown.Item
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
|
+
type MultiSessionAuthClient,
|
|
2
3
|
useAuth,
|
|
4
|
+
useAuthPlugin,
|
|
3
5
|
useListDeviceSessions,
|
|
4
6
|
useSession
|
|
5
7
|
} from "@better-auth-ui/react"
|
|
6
|
-
import { Card, type CardProps, cn
|
|
8
|
+
import { Card, type CardProps, cn } from "@heroui/react"
|
|
9
|
+
|
|
10
|
+
import { multiSessionPlugin } from "../../../lib/auth/multi-session-plugin"
|
|
11
|
+
|
|
7
12
|
import { ManageAccount } from "./manage-account"
|
|
8
13
|
|
|
9
14
|
export type ManageAccountsProps = {
|
|
@@ -23,16 +28,15 @@ export function ManageAccounts({
|
|
|
23
28
|
className,
|
|
24
29
|
variant,
|
|
25
30
|
...props
|
|
26
|
-
}: ManageAccountsProps & CardProps) {
|
|
27
|
-
const {
|
|
28
|
-
const {
|
|
31
|
+
}: ManageAccountsProps & Omit<CardProps, "children">) {
|
|
32
|
+
const { authClient } = useAuth()
|
|
33
|
+
const { localization: multiSessionLocalization } =
|
|
34
|
+
useAuthPlugin(multiSessionPlugin)
|
|
35
|
+
const { data: session } = useSession(authClient)
|
|
29
36
|
|
|
30
|
-
const { data: deviceSessions, isPending } = useListDeviceSessions(
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
return false
|
|
34
|
-
}
|
|
35
|
-
})
|
|
37
|
+
const { data: deviceSessions, isPending } = useListDeviceSessions(
|
|
38
|
+
authClient as MultiSessionAuthClient
|
|
39
|
+
)
|
|
36
40
|
|
|
37
41
|
const otherSessions = deviceSessions?.filter(
|
|
38
42
|
(deviceSession) => deviceSession.session.id !== session?.session.id
|
|
@@ -40,7 +44,7 @@ export function ManageAccounts({
|
|
|
40
44
|
|
|
41
45
|
const allRows = [
|
|
42
46
|
{
|
|
43
|
-
key: "current",
|
|
47
|
+
key: session?.session.id ?? "current",
|
|
44
48
|
deviceSession: !isPending ? session : null,
|
|
45
49
|
isPending
|
|
46
50
|
},
|
|
@@ -54,7 +58,7 @@ export function ManageAccounts({
|
|
|
54
58
|
return (
|
|
55
59
|
<div>
|
|
56
60
|
<h2 className={cn("text-sm font-semibold mb-3")}>
|
|
57
|
-
{
|
|
61
|
+
{multiSessionLocalization.manageAccounts}
|
|
58
62
|
</h2>
|
|
59
63
|
|
|
60
64
|
<Card className={cn(className)} variant={variant} {...props}>
|
|
@@ -1,32 +1,34 @@
|
|
|
1
1
|
import {
|
|
2
|
+
type MultiSessionAuthClient,
|
|
2
3
|
useAuth,
|
|
4
|
+
useAuthPlugin,
|
|
3
5
|
useListDeviceSessions,
|
|
4
6
|
useSession
|
|
5
7
|
} from "@better-auth-ui/react"
|
|
6
8
|
import { Check, CirclePlus } from "@gravity-ui/icons"
|
|
7
|
-
import { Dropdown, Label, Separator
|
|
9
|
+
import { Dropdown, Label, Separator } from "@heroui/react"
|
|
8
10
|
|
|
9
|
-
import {
|
|
10
|
-
import { UserView } from "
|
|
11
|
+
import { multiSessionPlugin } from "../../../lib/auth/multi-session-plugin"
|
|
12
|
+
import { UserView } from "../user/user-view"
|
|
13
|
+
import { SwitchAccountSubmenuItem } from "./switch-account-submenu-item"
|
|
11
14
|
|
|
12
15
|
/**
|
|
13
|
-
* Render the
|
|
16
|
+
* Render the submenu content for switching between multiple authenticated sessions.
|
|
14
17
|
*
|
|
15
18
|
* Shows the current session with a checkmark, lists other device sessions that can be activated,
|
|
16
19
|
* and provides an option to add a new account. This component should be rendered inside a
|
|
17
20
|
* Dropdown.SubmenuTrigger to defer the useListDeviceSessions query until the submenu is opened.
|
|
18
21
|
*
|
|
19
|
-
* @returns The switch account
|
|
22
|
+
* @returns The switch account submenu content as a JSX element
|
|
20
23
|
*/
|
|
21
|
-
export function
|
|
22
|
-
const { basePaths, viewPaths
|
|
23
|
-
const {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
})
|
|
24
|
+
export function SwitchAccountSubmenuContent() {
|
|
25
|
+
const { authClient, basePaths, viewPaths } = useAuth()
|
|
26
|
+
const { localization: multiSessionLocalization } =
|
|
27
|
+
useAuthPlugin(multiSessionPlugin)
|
|
28
|
+
const { data: session } = useSession(authClient)
|
|
29
|
+
const { data: deviceSessions, isPending } = useListDeviceSessions(
|
|
30
|
+
authClient as MultiSessionAuthClient
|
|
31
|
+
)
|
|
30
32
|
|
|
31
33
|
return (
|
|
32
34
|
<Dropdown.Popover className="min-w-40 md:min-w-56 max-w-[48svw]">
|
|
@@ -42,7 +44,7 @@ export function SwitchAccountMenu() {
|
|
|
42
44
|
(deviceSession) => deviceSession.session.id !== session?.session?.id
|
|
43
45
|
)
|
|
44
46
|
.map((deviceSession) => (
|
|
45
|
-
<
|
|
47
|
+
<SwitchAccountSubmenuItem
|
|
46
48
|
key={deviceSession.session.id}
|
|
47
49
|
deviceSession={deviceSession}
|
|
48
50
|
/>
|
|
@@ -51,12 +53,12 @@ export function SwitchAccountMenu() {
|
|
|
51
53
|
<Separator />
|
|
52
54
|
|
|
53
55
|
<Dropdown.Item
|
|
54
|
-
textValue={
|
|
56
|
+
textValue={multiSessionLocalization.addAccount}
|
|
55
57
|
href={`${basePaths.auth}/${viewPaths.auth.signIn}`}
|
|
56
58
|
>
|
|
57
59
|
<CirclePlus className="text-muted" />
|
|
58
60
|
|
|
59
|
-
<Label>{
|
|
61
|
+
<Label>{multiSessionLocalization.addAccount}</Label>
|
|
60
62
|
</Dropdown.Item>
|
|
61
63
|
</Dropdown.Menu>
|
|
62
64
|
</Dropdown.Popover>
|
|
@@ -1,14 +1,18 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
type MultiSessionAuthClient,
|
|
3
|
+
useAuth,
|
|
4
|
+
useSetActiveSession
|
|
5
|
+
} from "@better-auth-ui/react"
|
|
6
|
+
import { Dropdown, Spinner } from "@heroui/react"
|
|
3
7
|
import type { Session, User } from "better-auth"
|
|
4
|
-
import { UserView } from "
|
|
8
|
+
import { UserView } from "../user/user-view"
|
|
5
9
|
|
|
6
10
|
type DeviceSession = {
|
|
7
11
|
session: Session
|
|
8
12
|
user: User
|
|
9
13
|
}
|
|
10
14
|
|
|
11
|
-
export type
|
|
15
|
+
export type SwitchAccountSubmenuItemProps = {
|
|
12
16
|
deviceSession: DeviceSession
|
|
13
17
|
}
|
|
14
18
|
|
|
@@ -18,10 +22,16 @@ export type SwitchAccountItemProps = {
|
|
|
18
22
|
* @param deviceSession - The device session to display and switch to when pressed
|
|
19
23
|
* @returns The switch account dropdown item as a JSX element
|
|
20
24
|
*/
|
|
21
|
-
export function
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
})
|
|
25
|
+
export function SwitchAccountSubmenuItem({
|
|
26
|
+
deviceSession
|
|
27
|
+
}: SwitchAccountSubmenuItemProps) {
|
|
28
|
+
const { authClient } = useAuth()
|
|
29
|
+
const { mutate: setActiveSession, isPending } = useSetActiveSession(
|
|
30
|
+
authClient as MultiSessionAuthClient,
|
|
31
|
+
{
|
|
32
|
+
onSuccess: () => window.scrollTo({ top: 0 })
|
|
33
|
+
}
|
|
34
|
+
)
|
|
25
35
|
|
|
26
36
|
return (
|
|
27
37
|
<Dropdown.Item
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { useAuth, useAuthPlugin, useSession } from "@better-auth-ui/react"
|
|
2
|
+
import { Persons } from "@gravity-ui/icons"
|
|
3
|
+
import { Dropdown, Label } from "@heroui/react"
|
|
4
|
+
|
|
5
|
+
import { multiSessionPlugin } from "../../../lib/auth/multi-session-plugin"
|
|
6
|
+
|
|
7
|
+
import { SwitchAccountSubmenuContent } from "./switch-account-submenu-content"
|
|
8
|
+
|
|
9
|
+
export type SwitchAccountSubmenuProps = {
|
|
10
|
+
className?: string
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Render a dropdown submenu for switching between multiple authenticated sessions.
|
|
15
|
+
*
|
|
16
|
+
* This component renders as a submenu trigger that opens the switch account menu.
|
|
17
|
+
* It should be rendered inside a Dropdown.Menu.
|
|
18
|
+
*
|
|
19
|
+
* @param className - Optional additional CSS class names
|
|
20
|
+
* @returns The switch account submenu as a JSX element
|
|
21
|
+
*/
|
|
22
|
+
export function SwitchAccountSubmenu({ className }: SwitchAccountSubmenuProps) {
|
|
23
|
+
const { authClient } = useAuth()
|
|
24
|
+
const { data: session } = useSession(authClient)
|
|
25
|
+
const { localization: multiSessionLocalization } =
|
|
26
|
+
useAuthPlugin(multiSessionPlugin)
|
|
27
|
+
|
|
28
|
+
if (!session) {
|
|
29
|
+
return null
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<Dropdown.SubmenuTrigger>
|
|
34
|
+
<Dropdown.Item
|
|
35
|
+
className={className}
|
|
36
|
+
textValue={multiSessionLocalization.switchAccount}
|
|
37
|
+
>
|
|
38
|
+
<Persons className="text-muted" />
|
|
39
|
+
|
|
40
|
+
<Label>{multiSessionLocalization.switchAccount}</Label>
|
|
41
|
+
|
|
42
|
+
<Dropdown.SubmenuIndicator />
|
|
43
|
+
</Dropdown.Item>
|
|
44
|
+
|
|
45
|
+
<Dropdown.Popover className="min-w-40 md:min-w-56 max-w-[48svw]">
|
|
46
|
+
<SwitchAccountSubmenuContent />
|
|
47
|
+
</Dropdown.Popover>
|
|
48
|
+
</Dropdown.SubmenuTrigger>
|
|
49
|
+
)
|
|
50
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { type AuthView, authMutationKeys } from "@better-auth-ui/core"
|
|
2
|
+
import {
|
|
3
|
+
type PasskeyAuthClient,
|
|
4
|
+
useAuth,
|
|
5
|
+
useAuthPlugin,
|
|
6
|
+
useSignInPasskey
|
|
7
|
+
} from "@better-auth-ui/react"
|
|
8
|
+
import { Fingerprint } from "@gravity-ui/icons"
|
|
9
|
+
import { Button, Spinner } from "@heroui/react"
|
|
10
|
+
import { useIsMutating } from "@tanstack/react-query"
|
|
11
|
+
|
|
12
|
+
import { passkeyPlugin } from "../../../lib/auth/passkey-plugin"
|
|
13
|
+
|
|
14
|
+
export type PasskeyButtonProps = {
|
|
15
|
+
/** @remarks `AuthView` */
|
|
16
|
+
view?: AuthView
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function PasskeyButton({ view }: PasskeyButtonProps) {
|
|
20
|
+
const { authClient, localization, redirectTo, navigate } = useAuth()
|
|
21
|
+
const { localization: passkeyLocalization } = useAuthPlugin(passkeyPlugin)
|
|
22
|
+
|
|
23
|
+
const { mutate: signInPasskey, isPending: passkeyPending } = useSignInPasskey(
|
|
24
|
+
authClient as PasskeyAuthClient,
|
|
25
|
+
{
|
|
26
|
+
onSuccess: () => navigate({ to: redirectTo })
|
|
27
|
+
}
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
const signInMutating = useIsMutating({
|
|
31
|
+
mutationKey: authMutationKeys.signIn.all
|
|
32
|
+
})
|
|
33
|
+
const signUpMutating = useIsMutating({
|
|
34
|
+
mutationKey: authMutationKeys.signUp.all
|
|
35
|
+
})
|
|
36
|
+
const isPending = signInMutating + signUpMutating > 0
|
|
37
|
+
|
|
38
|
+
// Passkey sign-in isn't relevant on the sign-up flow.
|
|
39
|
+
if (view === "signUp") return null
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<Button
|
|
43
|
+
className="w-full"
|
|
44
|
+
variant="tertiary"
|
|
45
|
+
isDisabled={isPending}
|
|
46
|
+
isPending={passkeyPending}
|
|
47
|
+
onPress={() => signInPasskey()}
|
|
48
|
+
>
|
|
49
|
+
{passkeyPending ? <Spinner color="current" size="sm" /> : <Fingerprint />}
|
|
50
|
+
{localization.auth.continueWith.replace(
|
|
51
|
+
"{{provider}}",
|
|
52
|
+
passkeyLocalization.passkey
|
|
53
|
+
)}
|
|
54
|
+
</Button>
|
|
55
|
+
)
|
|
56
|
+
}
|
|
@@ -1,6 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
type PasskeyAuthClient,
|
|
3
|
+
useAuth,
|
|
4
|
+
useAuthPlugin,
|
|
5
|
+
useDeletePasskey
|
|
6
|
+
} from "@better-auth-ui/react"
|
|
2
7
|
import { Fingerprint, Xmark } from "@gravity-ui/icons"
|
|
3
|
-
import { Button, Spinner
|
|
8
|
+
import { Button, Spinner } from "@heroui/react"
|
|
9
|
+
|
|
10
|
+
import { passkeyPlugin } from "../../../lib/auth/passkey-plugin"
|
|
4
11
|
|
|
5
12
|
export type PasskeyProps = {
|
|
6
13
|
passkey: {
|
|
@@ -11,11 +18,14 @@ export type PasskeyProps = {
|
|
|
11
18
|
}
|
|
12
19
|
|
|
13
20
|
export function Passkey({ passkey }: PasskeyProps) {
|
|
14
|
-
const { localization } = useAuth()
|
|
21
|
+
const { authClient, localization } = useAuth()
|
|
22
|
+
const { localization: passkeyLocalization } = useAuthPlugin(passkeyPlugin)
|
|
23
|
+
|
|
24
|
+
const { mutate: deletePasskey, isPending } = useDeletePasskey(
|
|
25
|
+
authClient as PasskeyAuthClient
|
|
26
|
+
)
|
|
15
27
|
|
|
16
|
-
const
|
|
17
|
-
onError: (error) => toast.danger(error.error?.message || error.message)
|
|
18
|
-
})
|
|
28
|
+
const passkeyName = passkey.name || passkeyLocalization.passkey
|
|
19
29
|
|
|
20
30
|
return (
|
|
21
31
|
<div className="flex items-center gap-3">
|
|
@@ -24,9 +34,7 @@ export function Passkey({ passkey }: PasskeyProps) {
|
|
|
24
34
|
</div>
|
|
25
35
|
|
|
26
36
|
<div className="flex flex-col min-w-0">
|
|
27
|
-
<span className="text-sm font-medium leading-tight">
|
|
28
|
-
{passkey.name || localization.auth.passkey}
|
|
29
|
-
</span>
|
|
37
|
+
<span className="text-sm font-medium leading-tight">{passkeyName}</span>
|
|
30
38
|
|
|
31
39
|
<span className="text-xs text-muted">
|
|
32
40
|
{new Date(passkey.createdAt).toLocaleString(undefined, {
|
|
@@ -42,6 +50,10 @@ export function Passkey({ passkey }: PasskeyProps) {
|
|
|
42
50
|
size="sm"
|
|
43
51
|
isPending={isPending}
|
|
44
52
|
onPress={() => deletePasskey({ id: passkey.id })}
|
|
53
|
+
aria-label={passkeyLocalization.deletePasskey.replace(
|
|
54
|
+
"{{name}}",
|
|
55
|
+
passkeyName
|
|
56
|
+
)}
|
|
45
57
|
>
|
|
46
58
|
{isPending ? <Spinner color="current" size="sm" /> : <Xmark />}
|
|
47
59
|
{localization.settings.delete}
|