@atproto/oauth-provider-ui 0.1.0 → 0.1.1

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.
Files changed (194) hide show
  1. package/dist/authorization-page-Cms-rcBA.js +3 -0
  2. package/dist/authorization-page-Cms-rcBA.js.map +1 -0
  3. package/dist/bundle-manifest.json +630 -0
  4. package/dist/error-page-DC6Vc-cv.js +2 -0
  5. package/dist/error-page-DC6Vc-cv.js.map +1 -0
  6. package/dist/error-view-CRGNTAn2.css +1 -0
  7. package/dist/error-view-MVy7C9l0.js +59 -0
  8. package/dist/error-view-MVy7C9l0.js.map +1 -0
  9. package/dist/index-CHPoD7Rp.js +20 -0
  10. package/dist/index-CHPoD7Rp.js.map +1 -0
  11. package/dist/messages-B0mgsxS-.js +2 -0
  12. package/dist/messages-B0mgsxS-.js.map +1 -0
  13. package/dist/messages-B5g8Fkio.js +2 -0
  14. package/dist/messages-B5g8Fkio.js.map +1 -0
  15. package/dist/messages-BCMss-Kt.js +2 -0
  16. package/dist/messages-BCMss-Kt.js.map +1 -0
  17. package/dist/messages-BGUrKgyK.js +2 -0
  18. package/dist/messages-BGUrKgyK.js.map +1 -0
  19. package/dist/messages-BjxAnLDp.js +2 -0
  20. package/dist/messages-BjxAnLDp.js.map +1 -0
  21. package/dist/messages-Bjysz3rI.js +2 -0
  22. package/dist/messages-Bjysz3rI.js.map +1 -0
  23. package/dist/messages-BvvEr3UX.js +2 -0
  24. package/dist/messages-BvvEr3UX.js.map +1 -0
  25. package/dist/messages-Bz6JOhJf.js +2 -0
  26. package/dist/messages-Bz6JOhJf.js.map +1 -0
  27. package/dist/messages-BzL3D1EU.js +2 -0
  28. package/dist/messages-BzL3D1EU.js.map +1 -0
  29. package/dist/messages-CAvN5UoW.js +2 -0
  30. package/dist/messages-CAvN5UoW.js.map +1 -0
  31. package/dist/messages-CEmswT1Q.js +2 -0
  32. package/dist/messages-CEmswT1Q.js.map +1 -0
  33. package/dist/messages-CHYqz0q6.js +2 -0
  34. package/dist/messages-CHYqz0q6.js.map +1 -0
  35. package/dist/messages-CRmpdijj.js +2 -0
  36. package/dist/messages-CRmpdijj.js.map +1 -0
  37. package/dist/messages-Cdb79R6S.js +2 -0
  38. package/dist/messages-Cdb79R6S.js.map +1 -0
  39. package/dist/messages-ChkJ_0WT.js +2 -0
  40. package/dist/messages-ChkJ_0WT.js.map +1 -0
  41. package/dist/messages-CqiEX6JJ.js +2 -0
  42. package/dist/messages-CqiEX6JJ.js.map +1 -0
  43. package/dist/messages-CxkHjJSR.js +2 -0
  44. package/dist/messages-CxkHjJSR.js.map +1 -0
  45. package/dist/messages-D0-cWoJ9.js +2 -0
  46. package/dist/messages-D0-cWoJ9.js.map +1 -0
  47. package/dist/messages-D2MnAxYY.js +2 -0
  48. package/dist/messages-D2MnAxYY.js.map +1 -0
  49. package/dist/messages-D5TZVsui.js +2 -0
  50. package/dist/messages-D5TZVsui.js.map +1 -0
  51. package/dist/messages-DBdV4-iw.js +2 -0
  52. package/dist/messages-DBdV4-iw.js.map +1 -0
  53. package/dist/messages-DEK3zybC.js +2 -0
  54. package/dist/messages-DEK3zybC.js.map +1 -0
  55. package/dist/messages-DGSM5jkd.js +2 -0
  56. package/dist/messages-DGSM5jkd.js.map +1 -0
  57. package/dist/messages-DJgAnSTQ.js +2 -0
  58. package/dist/messages-DJgAnSTQ.js.map +1 -0
  59. package/dist/messages-DK7O7sb_.js +2 -0
  60. package/dist/messages-DK7O7sb_.js.map +1 -0
  61. package/dist/messages-DRp7qc3j.js +2 -0
  62. package/dist/messages-DRp7qc3j.js.map +1 -0
  63. package/dist/messages-DT6xRw0m.js +2 -0
  64. package/dist/messages-DT6xRw0m.js.map +1 -0
  65. package/dist/messages-LnzLtU0L.js +2 -0
  66. package/dist/messages-LnzLtU0L.js.map +1 -0
  67. package/dist/messages-_Nk2qNGw.js +2 -0
  68. package/dist/messages-_Nk2qNGw.js.map +1 -0
  69. package/dist/messages-eHH6nZyF.js +2 -0
  70. package/dist/messages-eHH6nZyF.js.map +1 -0
  71. package/dist/messages-iNw8zY2C.js +2 -0
  72. package/dist/messages-iNw8zY2C.js.map +1 -0
  73. package/dist/messages-ipc0L8yF.js +2 -0
  74. package/dist/messages-ipc0L8yF.js.map +1 -0
  75. package/dist/messages-j7LsWm2F.js +2 -0
  76. package/dist/messages-j7LsWm2F.js.map +1 -0
  77. package/dist/messages-mgE_5UEw.js +2 -0
  78. package/dist/messages-mgE_5UEw.js.map +1 -0
  79. package/dist/messages-oRd-J5--.js +2 -0
  80. package/dist/messages-oRd-J5--.js.map +1 -0
  81. package/package.json +10 -8
  82. package/.linguirc +0 -57
  83. package/CHANGELOG.md +0 -17
  84. package/CONTRIBUTING.md +0 -6
  85. package/authorization-page.html +0 -186
  86. package/error-page.html +0 -118
  87. package/index.html +0 -13
  88. package/src/authorization-page.tsx +0 -49
  89. package/src/components/forms/button-toggle-visibility.tsx +0 -43
  90. package/src/components/forms/button.tsx +0 -60
  91. package/src/components/forms/fieldset.tsx +0 -55
  92. package/src/components/forms/form-card-async.tsx +0 -103
  93. package/src/components/forms/form-card.tsx +0 -49
  94. package/src/components/forms/input-checkbox.tsx +0 -78
  95. package/src/components/forms/input-container.tsx +0 -107
  96. package/src/components/forms/input-email-address.tsx +0 -65
  97. package/src/components/forms/input-new-password.tsx +0 -62
  98. package/src/components/forms/input-password.tsx +0 -87
  99. package/src/components/forms/input-text.tsx +0 -82
  100. package/src/components/forms/input-token.tsx +0 -94
  101. package/src/components/forms/wizard-card.tsx +0 -116
  102. package/src/components/layouts/layout-title-page.tsx +0 -78
  103. package/src/components/layouts/layout-welcome.tsx +0 -78
  104. package/src/components/utils/account-identifier.tsx +0 -23
  105. package/src/components/utils/account-image.tsx +0 -33
  106. package/src/components/utils/admonition.tsx +0 -52
  107. package/src/components/utils/client-name.tsx +0 -71
  108. package/src/components/utils/error-card.tsx +0 -93
  109. package/src/components/utils/error-message.tsx +0 -88
  110. package/src/components/utils/help-card.tsx +0 -46
  111. package/src/components/utils/icons.tsx +0 -88
  112. package/src/components/utils/link-anchor.tsx +0 -28
  113. package/src/components/utils/link-title.tsx +0 -26
  114. package/src/components/utils/multi-lang-string.tsx +0 -62
  115. package/src/components/utils/password-strength-label.tsx +0 -37
  116. package/src/components/utils/password-strength-meter.tsx +0 -58
  117. package/src/components/utils/url-viewer.tsx +0 -73
  118. package/src/error-page.tsx +0 -23
  119. package/src/hooks/use-api.ts +0 -202
  120. package/src/hooks/use-async-action.ts +0 -120
  121. package/src/hooks/use-bound-dispatch.ts +0 -5
  122. package/src/hooks/use-browser-color-scheme.ts +0 -31
  123. package/src/hooks/use-random-string.ts +0 -37
  124. package/src/hooks/use-stepper.ts +0 -87
  125. package/src/lib/api.ts +0 -225
  126. package/src/lib/cookies.ts +0 -17
  127. package/src/lib/json-client.ts +0 -141
  128. package/src/lib/password.ts +0 -98
  129. package/src/lib/ref.ts +0 -17
  130. package/src/lib/util.ts +0 -14
  131. package/src/locales/an/messages.po +0 -494
  132. package/src/locales/ast/messages.po +0 -494
  133. package/src/locales/ca/messages.po +0 -494
  134. package/src/locales/da/messages.po +0 -494
  135. package/src/locales/de/messages.po +0 -494
  136. package/src/locales/el/messages.po +0 -494
  137. package/src/locales/en/messages.po +0 -494
  138. package/src/locales/en-GB/messages.po +0 -494
  139. package/src/locales/es/messages.po +0 -494
  140. package/src/locales/eu/messages.po +0 -494
  141. package/src/locales/fi/messages.po +0 -494
  142. package/src/locales/fr/messages.po +0 -494
  143. package/src/locales/ga/messages.po +0 -494
  144. package/src/locales/gl/messages.po +0 -494
  145. package/src/locales/hi/messages.po +0 -494
  146. package/src/locales/hu/messages.po +0 -494
  147. package/src/locales/ia/messages.po +0 -494
  148. package/src/locales/id/messages.po +0 -494
  149. package/src/locales/it/messages.po +0 -494
  150. package/src/locales/ja/messages.po +0 -494
  151. package/src/locales/km/messages.po +0 -494
  152. package/src/locales/ko/messages.po +0 -494
  153. package/src/locales/load.ts +0 -8
  154. package/src/locales/locale-provider.tsx +0 -108
  155. package/src/locales/locale-selector.tsx +0 -57
  156. package/src/locales/locales.ts +0 -183
  157. package/src/locales/ne/messages.po +0 -494
  158. package/src/locales/nl/messages.po +0 -494
  159. package/src/locales/pl/messages.po +0 -494
  160. package/src/locales/pt-BR/messages.po +0 -494
  161. package/src/locales/ro/messages.po +0 -494
  162. package/src/locales/ru/messages.po +0 -494
  163. package/src/locales/sv/messages.po +0 -494
  164. package/src/locales/th/messages.po +0 -494
  165. package/src/locales/tr/messages.po +0 -494
  166. package/src/locales/uk/messages.po +0 -494
  167. package/src/locales/vi/messages.po +0 -494
  168. package/src/locales/zh-CN/messages.po +0 -494
  169. package/src/locales/zh-HK/messages.po +0 -494
  170. package/src/locales/zh-TW/messages.po +0 -494
  171. package/src/style.css +0 -219
  172. package/src/views/authorize/accept/accept-form.tsx +0 -155
  173. package/src/views/authorize/accept/accept-view.tsx +0 -70
  174. package/src/views/authorize/authorize-view.tsx +0 -186
  175. package/src/views/authorize/reset-password/reset-password-confirm-form.tsx +0 -88
  176. package/src/views/authorize/reset-password/reset-password-request-form.tsx +0 -80
  177. package/src/views/authorize/reset-password/reset-password-view.tsx +0 -127
  178. package/src/views/authorize/sign-in/sign-in-form.tsx +0 -240
  179. package/src/views/authorize/sign-in/sign-in-picker.tsx +0 -116
  180. package/src/views/authorize/sign-in/sign-in-view.tsx +0 -145
  181. package/src/views/authorize/sign-up/sign-up-account-form.tsx +0 -142
  182. package/src/views/authorize/sign-up/sign-up-disclaimer.tsx +0 -51
  183. package/src/views/authorize/sign-up/sign-up-handle-form.tsx +0 -287
  184. package/src/views/authorize/sign-up/sign-up-hcaptcha-form.tsx +0 -108
  185. package/src/views/authorize/sign-up/sign-up-view.tsx +0 -158
  186. package/src/views/authorize/welcome/welcome-view.tsx +0 -56
  187. package/src/views/error/error-view.tsx +0 -31
  188. package/tsconfig.json +0 -7
  189. package/tsconfig.src.json +0 -13
  190. package/tsconfig.src.tsbuildinfo +0 -1
  191. package/tsconfig.tools.json +0 -8
  192. package/tsconfig.tools.tsbuildinfo +0 -1
  193. package/vite.config.mjs +0 -47
  194. /package/{src/hydration-data.d.ts → hydration-data.d.ts} +0 -0
@@ -1,88 +0,0 @@
1
- import { Trans } from '@lingui/react/macro'
2
- import { useRef, useState } from 'react'
3
- import { Fieldset } from '../../../components/forms/fieldset.tsx'
4
- import {
5
- FormCardAsync,
6
- FormCardAsyncProps,
7
- } from '../../../components/forms/form-card-async.tsx'
8
- import { InputNewPassword } from '../../../components/forms/input-new-password.tsx'
9
- import { InputToken } from '../../../components/forms/input-token.tsx'
10
- import { Admonition } from '../../../components/utils/admonition.tsx'
11
- import { useRandomString } from '../../../hooks/use-random-string.ts'
12
- import { Override } from '../../../lib/util.ts'
13
-
14
- export type ResetPasswordConfirmFormProps = Override<
15
- FormCardAsyncProps,
16
- {
17
- onSubmit: (
18
- data: {
19
- token: string
20
- password: string
21
- },
22
- signal: AbortSignal,
23
- ) => void | PromiseLike<void>
24
-
25
- tokenPattern?: string
26
- tokenFormat?: string
27
- tokenParseValue?: (value: string) => string | false
28
- }
29
- >
30
-
31
- export function ResetPasswordConfirmForm({
32
- onSubmit,
33
-
34
- // FormCardAsyncProps
35
- invalid,
36
- ...props
37
- }: ResetPasswordConfirmFormProps) {
38
- const tokenAriaId = useRandomString({ prefix: 'reset-pwd-email-' })
39
- const passwordRef = useRef<HTMLInputElement>(null)
40
-
41
- const [token, setToken] = useState<string | null>(null)
42
- const [password, setPassword] = useState<string | undefined>(undefined)
43
-
44
- return (
45
- <FormCardAsync
46
- {...props}
47
- onSubmit={(signal) => {
48
- if (token && password) return onSubmit({ token, password }, signal)
49
- }}
50
- invalid={invalid || !token || !password}
51
- >
52
- <Admonition role="info">
53
- <p id={tokenAriaId} className="text-md">
54
- <Trans>
55
- You will receive an email with a "reset code". Enter that code here
56
- then enter your new password.
57
- </Trans>
58
- </p>
59
- </Admonition>
60
-
61
- <Fieldset label={<Trans>Reset code</Trans>}>
62
- <InputToken
63
- name="code"
64
- aria-labelledby={tokenAriaId}
65
- enterKeyHint="next"
66
- required
67
- autoFocus={true}
68
- onToken={(token) => {
69
- setToken(token)
70
- // Auto-focus next field when token is complete
71
- if (token) passwordRef.current?.focus()
72
- }}
73
- />
74
- </Fieldset>
75
-
76
- <Fieldset label={<Trans>New password</Trans>}>
77
- <InputNewPassword
78
- ref={passwordRef}
79
- name="password"
80
- enterKeyHint="done"
81
- required
82
- password={password}
83
- onPassword={setPassword}
84
- />
85
- </Fieldset>
86
- </FormCardAsync>
87
- )
88
- }
@@ -1,80 +0,0 @@
1
- import { Trans, useLingui } from '@lingui/react/macro'
2
- import { useCallback, useRef, useState } from 'react'
3
- import { Fieldset } from '../../../components/forms/fieldset.tsx'
4
- import {
5
- AsyncActionController,
6
- FormCardAsync,
7
- FormCardAsyncProps,
8
- } from '../../../components/forms/form-card-async.tsx'
9
- import { InputEmailAddress } from '../../../components/forms/input-email-address.tsx'
10
- import { Admonition } from '../../../components/utils/admonition.tsx'
11
- import { useRandomString } from '../../../hooks/use-random-string.ts'
12
- import { mergeRefs } from '../../../lib/ref.ts'
13
- import { Override } from '../../../lib/util.ts'
14
-
15
- export type ResetPasswordRequestFormProps = Override<
16
- Omit<FormCardAsyncProps, 'children'>,
17
- {
18
- emailDefault?: string
19
- onSubmit: (
20
- data: { email: string },
21
- signal: AbortSignal,
22
- ) => void | PromiseLike<void>
23
- }
24
- >
25
-
26
- export function ResetPasswordRequestForm({
27
- emailDefault,
28
- onSubmit,
29
-
30
- // FormCardAsyncProps
31
- invalid,
32
- ref,
33
- ...props
34
- }: ResetPasswordRequestFormProps) {
35
- const { t } = useLingui()
36
- const emailAriaId = useRandomString({ prefix: 'reset-pwd-email-' })
37
- const [email, setEmail] = useState(emailDefault)
38
-
39
- const ctrlRef = useRef<AsyncActionController>(null)
40
-
41
- const doSubmit = useCallback(
42
- (signal: AbortSignal) => {
43
- if (email) return onSubmit({ email }, signal)
44
- },
45
- [email, onSubmit],
46
- )
47
-
48
- return (
49
- <FormCardAsync
50
- {...props}
51
- ref={mergeRefs([ref, ctrlRef])}
52
- invalid={invalid || !email}
53
- onSubmit={doSubmit}
54
- >
55
- <Fieldset label={<Trans>Email address</Trans>}>
56
- <InputEmailAddress
57
- name="email"
58
- placeholder={t`Enter your email address`}
59
- aria-labelledby={emailAriaId}
60
- title={t`Email address`}
61
- required
62
- autoFocus={true}
63
- value={email}
64
- onEmail={(email) => {
65
- ctrlRef.current?.reset()
66
- setEmail(email)
67
- }}
68
- />
69
- <Admonition role="info">
70
- <p id={emailAriaId} className="">
71
- <Trans>
72
- Enter the email you used to create your account. We'll send you a
73
- "reset code" so you can set a new password.
74
- </Trans>
75
- </p>
76
- </Admonition>
77
- </Fieldset>
78
- </FormCardAsync>
79
- )
80
- }
@@ -1,127 +0,0 @@
1
- import { Trans, useLingui } from '@lingui/react/macro'
2
- import { useState } from 'react'
3
- import { Button } from '../../../components/forms/button.tsx'
4
- import {
5
- LayoutTitlePage,
6
- LayoutTitlePageProps,
7
- } from '../../../components/layouts/layout-title-page.tsx'
8
- import { Override } from '../../../lib/util.ts'
9
- import { ResetPasswordConfirmForm } from './reset-password-confirm-form.tsx'
10
- import { ResetPasswordRequestForm } from './reset-password-request-form.tsx'
11
-
12
- export type ResetPasswordViewProps = Override<
13
- LayoutTitlePageProps,
14
- {
15
- emailDefault?: string
16
- onresetPasswordRequest: (
17
- data: { email: string },
18
- signal: AbortSignal,
19
- ) => void | PromiseLike<void>
20
- onResetPasswordConfirm: (
21
- data: {
22
- token: string
23
- password: string
24
- },
25
- signal: AbortSignal,
26
- ) => void | PromiseLike<void>
27
- onBack: () => void
28
- }
29
- >
30
-
31
- enum View {
32
- RequestReset,
33
- ConfirmReset,
34
- PasswordUpdated,
35
- }
36
-
37
- export function ResetPasswordView({
38
- emailDefault,
39
- onresetPasswordRequest,
40
- onResetPasswordConfirm,
41
- onBack,
42
-
43
- // LayoutTitlePage
44
- ...props
45
- }: ResetPasswordViewProps) {
46
- const { t } = useLingui()
47
- const [view, setView] = useState<View>(View.RequestReset)
48
-
49
- if (view === View.RequestReset) {
50
- return (
51
- <LayoutTitlePage
52
- {...props}
53
- title={props.title || t`Forgot Password`}
54
- subtitle={
55
- props.subtitle || <Trans>Let's get your password reset!</Trans>
56
- }
57
- >
58
- <ResetPasswordRequestForm
59
- emailDefault={emailDefault}
60
- submitLabel={<Trans>Next</Trans>}
61
- onSubmit={async (data, signal) => {
62
- await onresetPasswordRequest(data, signal)
63
- if (!signal.aborted) setView(View.ConfirmReset)
64
- }}
65
- cancelLabel={<Trans>Back</Trans>}
66
- onCancel={onBack}
67
- />
68
- <hr className="my-5 border-gray-300 dark:border-gray-700" />
69
- <center>
70
- <Button transparent onClick={() => setView(View.ConfirmReset)}>
71
- <Trans>Already have a code?</Trans>
72
- </Button>
73
- </center>
74
- </LayoutTitlePage>
75
- )
76
- }
77
-
78
- if (view === View.ConfirmReset) {
79
- return (
80
- <LayoutTitlePage
81
- {...props}
82
- title={props.title || t`Reset Password`}
83
- subtitle={
84
- props.subtitle || (
85
- <Trans>Enter the code you received to reset your password.</Trans>
86
- )
87
- }
88
- >
89
- <ResetPasswordConfirmForm
90
- submitLabel={<Trans>Next</Trans>}
91
- onSubmit={async (data, signal) => {
92
- await onResetPasswordConfirm(data, signal)
93
- if (!signal.aborted) setView(View.PasswordUpdated)
94
- }}
95
- cancelLabel={<Trans>Back</Trans>}
96
- onCancel={onBack}
97
- />
98
- </LayoutTitlePage>
99
- )
100
- }
101
-
102
- if (view === View.PasswordUpdated) {
103
- return (
104
- <LayoutTitlePage
105
- {...props}
106
- title={props.title || t`Password Updated`}
107
- subtitle={
108
- props.subtitle || <Trans>Your password has been updated!</Trans>
109
- }
110
- >
111
- <center>
112
- <h2 className="pb-2 text-xl font-bold">
113
- <Trans>Password updated!</Trans>
114
- </h2>
115
- <p className="pb-4">
116
- <Trans>You can now sign in with your new password.</Trans>
117
- </p>
118
- <Button color="primary" onClick={onBack}>
119
- <Trans>Okay</Trans>
120
- </Button>
121
- </center>
122
- </LayoutTitlePage>
123
- )
124
- }
125
-
126
- throw new Error(`Invalid view: ${view}`)
127
- }
@@ -1,240 +0,0 @@
1
- import { Trans, useLingui } from '@lingui/react/macro'
2
- import { useCallback, useRef, useState } from 'react'
3
- import { Button } from '../../../components/forms/button.tsx'
4
- import { Fieldset } from '../../../components/forms/fieldset.tsx'
5
- import {
6
- FormCardAsync,
7
- FormCardAsyncProps,
8
- } from '../../../components/forms/form-card-async.tsx'
9
- import { InputCheckbox } from '../../../components/forms/input-checkbox.tsx'
10
- import { InputPassword } from '../../../components/forms/input-password.tsx'
11
- import { InputText } from '../../../components/forms/input-text.tsx'
12
- import { InputToken } from '../../../components/forms/input-token.tsx'
13
- import { Admonition } from '../../../components/utils/admonition.tsx'
14
- import { AtSymbolIcon } from '../../../components/utils/icons.tsx'
15
- import { AsyncActionController } from '../../../hooks/use-async-action.ts'
16
- import {
17
- InvalidCredentialsError,
18
- SecondAuthenticationFactorRequiredError,
19
- } from '../../../lib/api.ts'
20
- import { mergeRefs } from '../../../lib/ref.ts'
21
- import { Override } from '../../../lib/util.ts'
22
-
23
- export type SignInFormOutput = {
24
- username: string
25
- password: string
26
- remember?: boolean
27
- }
28
-
29
- export type SignInFormProps = Override<
30
- Omit<FormCardAsyncProps, 'append' | 'onCancel'>,
31
- {
32
- usernameDefault?: string
33
- usernameReadonly?: boolean
34
- rememberDefault?: boolean
35
-
36
- onBack?: () => void
37
- onForgotPassword?: (emailHint?: string) => void
38
- onSubmit: (
39
- credentials: SignInFormOutput,
40
- signal: AbortSignal,
41
- ) => void | PromiseLike<void>
42
- }
43
- >
44
-
45
- export function SignInForm({
46
- usernameDefault = '',
47
- usernameReadonly = false,
48
- rememberDefault = false,
49
-
50
- onSubmit,
51
- onBack,
52
- onForgotPassword,
53
-
54
- // FormCardAsync
55
- ref,
56
- invalid,
57
- children,
58
- ...props
59
- }: SignInFormProps) {
60
- const { t } = useLingui()
61
-
62
- const [username, setUsername] = useState<string>(usernameDefault)
63
- const [password, setPassword] = useState<string>('')
64
- const [remember, setRemember] = useState<boolean>(rememberDefault)
65
- const [otp, setOtp] = useState<string | null>(null)
66
-
67
- const [secondFactor, setSecondFactor] =
68
- useState<null | SecondAuthenticationFactorRequiredError>(null)
69
-
70
- const [loading, setLoading] = useState(false)
71
-
72
- const formRef = useRef<AsyncActionController>(null)
73
-
74
- const clearSecondFactor = useCallback(() => {
75
- setOtp(null)
76
- setSecondFactor(null)
77
- }, [setOtp, setSecondFactor])
78
-
79
- const resetState = useCallback(() => {
80
- clearSecondFactor()
81
- formRef.current?.reset()
82
- }, [clearSecondFactor, formRef])
83
-
84
- const doSubmit = useCallback(
85
- async (signal: AbortSignal) => {
86
- try {
87
- await onSubmit(
88
- {
89
- username,
90
- password,
91
- remember,
92
- ...(secondFactor ? { [secondFactor.type]: otp } : {}),
93
- },
94
- signal,
95
- )
96
- } catch (err) {
97
- if (signal.aborted) {
98
- // If the action was aborted, ignore the error
99
- return
100
- }
101
-
102
- if (err instanceof SecondAuthenticationFactorRequiredError) {
103
- setSecondFactor(err)
104
-
105
- // Do not re-throw 2FA required error to prevent the form from from
106
- // displaying it. Instead, we handle the error by showing the second
107
- // factor form.
108
- return
109
- }
110
-
111
- if (err instanceof InvalidCredentialsError) {
112
- // If the username/password are not valid, clear the second factor
113
- // as valid credentials are a pre-requisite for 2FA.
114
- clearSecondFactor()
115
- }
116
-
117
- // Any thrown err will be displayed through the form's errorRender
118
- throw err
119
- }
120
- },
121
- [username, password, remember, secondFactor, otp, onSubmit],
122
- )
123
-
124
- return (
125
- <FormCardAsync
126
- {...props}
127
- ref={mergeRefs([ref, formRef])}
128
- onLoading={setLoading}
129
- onCancel={onBack}
130
- cancelLabel={t`Back`}
131
- append={children}
132
- invalid={
133
- invalid || !username || !password || (secondFactor != null && !otp)
134
- }
135
- submitLabel={secondFactor ? t`Confirm` : t`Sign in`}
136
- onSubmit={doSubmit}
137
- >
138
- <Fieldset disabled={loading} label={<Trans>Identifier</Trans>}>
139
- <InputText
140
- icon={<AtSymbolIcon className="w-5" />}
141
- name="username"
142
- type="text"
143
- title={t`Username or email address`}
144
- autoCapitalize="none"
145
- autoCorrect="off"
146
- autoComplete="username"
147
- spellCheck="false"
148
- dir="auto"
149
- enterKeyHint="next"
150
- required
151
- readOnly={usernameReadonly}
152
- disabled={usernameReadonly}
153
- autoFocus
154
- value={username}
155
- onChange={(event) => {
156
- resetState()
157
- setUsername(event.target.value)
158
- }}
159
- />
160
- </Fieldset>
161
-
162
- <Fieldset disabled={loading} label={<Trans>Password</Trans>}>
163
- <InputPassword
164
- name="password"
165
- onChange={(event) => {
166
- resetState()
167
- setPassword(event.target.value)
168
- }}
169
- append={
170
- onForgotPassword && (
171
- <Button
172
- className="text-sm"
173
- type="button"
174
- onClick={() => {
175
- onForgotPassword(
176
- username?.includes('@') ? username : undefined,
177
- )
178
- }}
179
- aria-label={t`Reset your password`}
180
- >
181
- <Trans>Forgot?</Trans>
182
- </Button>
183
- )
184
- }
185
- enterKeyHint={secondFactor ? 'next' : 'done'}
186
- disabled={loading}
187
- required
188
- />
189
- </Fieldset>
190
-
191
- <Admonition role="status">
192
- <p className="text-md text-primary pb-1 font-bold">
193
- <Trans>Warning</Trans>
194
- </p>
195
- <p className="text-sm">
196
- <Trans>
197
- Please verify the domain name of the website before entering your
198
- password. Never enter your password on a domain you do not trust.
199
- </Trans>
200
- </p>
201
- </Admonition>
202
-
203
- <InputCheckbox
204
- name="remember"
205
- title={t`Remember this account on this device`}
206
- enterKeyHint={secondFactor ? 'next' : 'done'}
207
- checked={remember}
208
- onChange={(event) => setRemember(event.target.checked)}
209
- >
210
- <Trans>Remember this account on this device</Trans>
211
- </InputCheckbox>
212
-
213
- {secondFactor && (
214
- <Fieldset
215
- key="2fa"
216
- disabled={loading}
217
- label={<Trans>2FA Confirmation</Trans>}
218
- >
219
- <div>
220
- <InputToken
221
- title={t`Confirmation code`}
222
- enterKeyHint="done"
223
- required
224
- autoFocus={true}
225
- value={otp ?? ''}
226
- onToken={setOtp}
227
- />
228
-
229
- <p className="text-sm text-slate-600 dark:text-slate-400">
230
- <Trans>
231
- Check your {secondFactor.hint} email for a login code and enter
232
- it here.
233
- </Trans>
234
- </p>
235
- </div>
236
- </Fieldset>
237
- )}
238
- </FormCardAsync>
239
- )
240
- }
@@ -1,116 +0,0 @@
1
- import { Trans, useLingui } from '@lingui/react/macro'
2
- import type { Account } from '@atproto/oauth-provider-api'
3
- import { Button } from '../../../components/forms/button.tsx'
4
- import {
5
- FormCard,
6
- FormCardProps,
7
- } from '../../../components/forms/form-card.tsx'
8
- import { InputContainer } from '../../../components/forms/input-container.tsx'
9
- import { AccountImage } from '../../../components/utils/account-image.tsx'
10
- import {
11
- AtSymbolIcon,
12
- CaretRightIcon,
13
- } from '../../../components/utils/icons.tsx'
14
- import { Override } from '../../../lib/util.ts'
15
-
16
- export type SignInPickerProps = Override<
17
- Omit<FormCardProps, 'cancel' | 'actions' | 'append'>,
18
- {
19
- accounts: readonly Account[]
20
-
21
- onAccount: (account: Account) => void
22
- onOther?: () => void
23
- onBack?: () => void
24
- }
25
- >
26
-
27
- export function SignInPicker({
28
- accounts,
29
-
30
- onAccount,
31
- onOther = undefined,
32
- onBack,
33
-
34
- // FormCard
35
- children,
36
- ...props
37
- }: SignInPickerProps) {
38
- const { t } = useLingui()
39
- return (
40
- <FormCard
41
- {...props}
42
- append={children}
43
- actions={null}
44
- cancel={
45
- onBack && (
46
- <Button onClick={onBack}>
47
- <Trans>Back</Trans>
48
- </Button>
49
- )
50
- }
51
- >
52
- <p className="text-sm font-medium text-slate-600 dark:text-slate-400">
53
- <Trans>Sign in as...</Trans>
54
- </p>
55
-
56
- {accounts.map((account) => {
57
- const [name, identifier] = [
58
- account.name,
59
- account.preferred_username,
60
- account.email,
61
- account.sub,
62
- ].filter(Boolean) as [string, string?]
63
-
64
- return (
65
- <InputContainer
66
- tabIndex={0}
67
- key={account.sub}
68
- onKeyDown={(event) => {
69
- if (event.key === 'Enter' || event.key === ' ') {
70
- onAccount(account)
71
- }
72
- }}
73
- onClick={() => onAccount(account)}
74
- role="button"
75
- aria-label={t`Sign in as ${account.name}`}
76
- icon={<AccountImage src={account.picture} alt={name} />}
77
- append={<CaretRightIcon aria-hidden className="h-4" />}
78
- >
79
- <span className="flex flex-wrap items-center">
80
- <span className="mr-2 truncate font-medium" arial-label={t`Name`}>
81
- {name}
82
- </span>
83
- {identifier && (
84
- <span
85
- className="truncate text-sm text-neutral-500 dark:text-neutral-400"
86
- arial-label={t`Identifier`}
87
- >
88
- {identifier}
89
- </span>
90
- )}
91
- </span>
92
- </InputContainer>
93
- )
94
- })}
95
-
96
- {onOther && (
97
- <InputContainer
98
- key="other"
99
- tabIndex={0}
100
- onKeyDown={(event) => {
101
- if (event.key === 'Enter' || event.key === ' ') onOther()
102
- }}
103
- onClick={onOther}
104
- aria-label={t`Login to account that is not listed`}
105
- role="button"
106
- append={<CaretRightIcon aria-hidden className="h-4" />}
107
- icon={<AtSymbolIcon aria-hidden className="h-4" />}
108
- >
109
- <span className="truncate text-slate-700 dark:text-slate-400">
110
- <Trans>Another account</Trans>
111
- </span>
112
- </InputContainer>
113
- )}
114
- </FormCard>
115
- )
116
- }