@nymphjs/tilmeld-components 1.0.0-beta.13 → 1.0.0-beta.131
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/CHANGELOG.md +494 -0
- package/README.md +6 -4
- package/dist/Account.svelte +516 -0
- package/dist/ChangePassword.svelte +309 -0
- package/dist/Login.svelte +603 -0
- package/dist/Recover.svelte +463 -0
- package/dist/RevokeTokens.svelte +236 -0
- package/dist/TwoFactor.svelte +544 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +8 -0
- package/{lib → dist}/index.js.map +1 -1
- package/jest.config.js +32 -0
- package/package.json +54 -43
- package/src/Account.svelte +280 -113
- package/src/ChangePassword.svelte +147 -51
- package/src/Login.svelte +328 -115
- package/src/Recover.svelte +180 -78
- package/src/RevokeTokens.svelte +236 -0
- package/src/TwoFactor.svelte +544 -0
- package/src/index.d.ts +3 -1
- package/src/index.ts +3 -1
- package/tsconfig.json +8 -4
- package/typedoc.json +4 -0
- package/dist/index.cjs +0 -3
- package/dist/index.cjs.LICENSE.txt +0 -45
- package/dist/index.cjs.map +0 -1
- package/jest.config.cjs +0 -6
- package/lib/Account.svelte +0 -333
- package/lib/Account.svelte.map +0 -1
- package/lib/ChangePassword.svelte +0 -188
- package/lib/ChangePassword.svelte.map +0 -1
- package/lib/Login.svelte +0 -353
- package/lib/Login.svelte.map +0 -1
- package/lib/Recover.svelte +0 -339
- package/lib/Recover.svelte.map +0 -1
- package/lib/index.d.ts +0 -5
- package/lib/index.js +0 -6
- package/webpack.config.cjs +0 -35
package/src/Login.svelte
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
<svelte:options runes />
|
|
2
|
+
|
|
1
3
|
<div
|
|
2
4
|
style="width: {width}; {style}"
|
|
3
5
|
use:useActions={use}
|
|
4
|
-
|
|
5
|
-
{...exclude($$restProps, [
|
|
6
|
+
{...exclude(restProps, [
|
|
6
7
|
'successRegisteredMessage$',
|
|
7
8
|
'successLoginMessage$',
|
|
8
9
|
'username$',
|
|
@@ -11,6 +12,8 @@
|
|
|
11
12
|
'name$',
|
|
12
13
|
'email$',
|
|
13
14
|
'phone$',
|
|
15
|
+
'code$',
|
|
16
|
+
'codeMessage$',
|
|
14
17
|
'loginButton$',
|
|
15
18
|
'registerButton$',
|
|
16
19
|
'registerLink$',
|
|
@@ -20,46 +23,82 @@
|
|
|
20
23
|
'progress$',
|
|
21
24
|
])}
|
|
22
25
|
>
|
|
23
|
-
{#if clientConfig == null ||
|
|
26
|
+
{#if $clientConfig == null || loading}
|
|
24
27
|
<div style="display: flex; justify-content: center; align-items: center;">
|
|
25
28
|
<CircularProgress
|
|
26
29
|
style="height: 32px; width: 32px;"
|
|
27
30
|
indeterminate
|
|
28
|
-
{...prefixFilter(
|
|
31
|
+
{...prefixFilter(restProps, 'progress$')}
|
|
29
32
|
/>
|
|
30
33
|
</div>
|
|
31
34
|
{:else if successRegisteredMessage}
|
|
32
|
-
<div {...prefixFilter(
|
|
35
|
+
<div {...prefixFilter(restProps, 'successRegisteredMessage$')}>
|
|
33
36
|
{successRegisteredMessage}
|
|
34
37
|
</div>
|
|
35
38
|
{:else if successLoginMessage}
|
|
36
|
-
<div {...prefixFilter(
|
|
39
|
+
<div {...prefixFilter(restProps, 'successLoginMessage$')}>
|
|
37
40
|
{successLoginMessage}
|
|
38
41
|
</div>
|
|
42
|
+
{:else if totp}
|
|
43
|
+
<form onsubmit={(e) => e.preventDefault()}>
|
|
44
|
+
<div {...prefixFilter(restProps, 'codeMessage$')}>
|
|
45
|
+
Enter the 6-digit 2FA code from your authenticator app.
|
|
46
|
+
</div>
|
|
47
|
+
|
|
48
|
+
<div>
|
|
49
|
+
<Textfield
|
|
50
|
+
bind:value={code}
|
|
51
|
+
label="2FA Code"
|
|
52
|
+
style="width: 100%;"
|
|
53
|
+
input$autocomplete="one-time-code"
|
|
54
|
+
input$name="one-time-code"
|
|
55
|
+
{...prefixFilter(restProps, 'code$')}
|
|
56
|
+
/>
|
|
57
|
+
</div>
|
|
58
|
+
|
|
59
|
+
{#if failureMessage}
|
|
60
|
+
<div class="tilmeld-login-failure">
|
|
61
|
+
{failureMessage}
|
|
62
|
+
</div>
|
|
63
|
+
{/if}
|
|
64
|
+
|
|
65
|
+
<div class="tilmeld-login-buttons">
|
|
66
|
+
<Button
|
|
67
|
+
variant="raised"
|
|
68
|
+
type="submit"
|
|
69
|
+
onclick={login}
|
|
70
|
+
{...prefixFilter(restProps, 'loginButton$')}
|
|
71
|
+
>
|
|
72
|
+
<Label>Log In</Label>
|
|
73
|
+
</Button>
|
|
74
|
+
</div>
|
|
75
|
+
</form>
|
|
39
76
|
{:else}
|
|
40
|
-
<form
|
|
77
|
+
<form onsubmit={(e) => e.preventDefault()}>
|
|
41
78
|
<div>
|
|
42
79
|
<Textfield
|
|
43
80
|
bind:value={username}
|
|
44
81
|
bind:this={usernameElem}
|
|
45
|
-
label={clientConfig.emailUsernames ? 'Email' : 'Username'}
|
|
46
|
-
type={clientConfig.emailUsernames ? 'email' : 'text'}
|
|
82
|
+
label={$clientConfig.emailUsernames ? 'Email' : 'Username'}
|
|
83
|
+
type={$clientConfig.emailUsernames ? 'email' : 'text'}
|
|
47
84
|
style="width: 100%;"
|
|
48
85
|
helperLine$style="width: 100%;"
|
|
49
86
|
invalid={usernameVerified === false}
|
|
50
|
-
input$autocomplete={clientConfig.emailUsernames
|
|
87
|
+
input$autocomplete={$clientConfig.emailUsernames
|
|
51
88
|
? 'email'
|
|
52
89
|
: 'username'}
|
|
53
|
-
input$name={clientConfig.emailUsernames ? 'email' : 'username'}
|
|
90
|
+
input$name={$clientConfig.emailUsernames ? 'email' : 'username'}
|
|
54
91
|
input$autocapitalize="off"
|
|
55
92
|
input$spellcheck="false"
|
|
56
|
-
{...prefixFilter(
|
|
93
|
+
{...prefixFilter(restProps, 'username$')}
|
|
57
94
|
>
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
{
|
|
61
|
-
|
|
62
|
-
|
|
95
|
+
{#snippet helper()}
|
|
96
|
+
<HelperText persistent>
|
|
97
|
+
{#if mode !== 'login'}
|
|
98
|
+
{usernameVerifiedMessage || ''}
|
|
99
|
+
{/if}
|
|
100
|
+
</HelperText>
|
|
101
|
+
{/snippet}
|
|
63
102
|
</Textfield>
|
|
64
103
|
</div>
|
|
65
104
|
|
|
@@ -69,16 +108,16 @@
|
|
|
69
108
|
label="Password"
|
|
70
109
|
type="password"
|
|
71
110
|
style="width: 100%;"
|
|
72
|
-
input$autocomplete="{clientConfig.allowRegistration &&
|
|
111
|
+
input$autocomplete="{$clientConfig.allowRegistration &&
|
|
73
112
|
mode !== 'login'
|
|
74
113
|
? 'new'
|
|
75
114
|
: 'current'}-password"
|
|
76
115
|
input$name="password"
|
|
77
|
-
{...prefixFilter(
|
|
116
|
+
{...prefixFilter(restProps, 'password$')}
|
|
78
117
|
/>
|
|
79
118
|
</div>
|
|
80
119
|
|
|
81
|
-
{#if clientConfig.allowRegistration && mode !== 'login'}
|
|
120
|
+
{#if $clientConfig.allowRegistration && mode !== 'login'}
|
|
82
121
|
<div>
|
|
83
122
|
<Textfield
|
|
84
123
|
bind:value={password2}
|
|
@@ -87,11 +126,11 @@
|
|
|
87
126
|
style="width: 100%;"
|
|
88
127
|
input$autocomplete="new-password"
|
|
89
128
|
input$name="password2"
|
|
90
|
-
{...prefixFilter(
|
|
129
|
+
{...prefixFilter(restProps, 'password2$')}
|
|
91
130
|
/>
|
|
92
131
|
</div>
|
|
93
132
|
|
|
94
|
-
{#if clientConfig.regFields.
|
|
133
|
+
{#if $clientConfig.regFields.includes('name')}
|
|
95
134
|
<div>
|
|
96
135
|
<Textfield
|
|
97
136
|
bind:value={name}
|
|
@@ -100,12 +139,12 @@
|
|
|
100
139
|
style="width: 100%;"
|
|
101
140
|
input$autocomplete="name"
|
|
102
141
|
input$name="name"
|
|
103
|
-
{...prefixFilter(
|
|
142
|
+
{...prefixFilter(restProps, 'name$')}
|
|
104
143
|
/>
|
|
105
144
|
</div>
|
|
106
145
|
{/if}
|
|
107
146
|
|
|
108
|
-
{#if
|
|
147
|
+
{#if !$clientConfig.emailUsernames && $clientConfig.regFields.includes('email')}
|
|
109
148
|
<div>
|
|
110
149
|
<Textfield
|
|
111
150
|
bind:value={email}
|
|
@@ -116,12 +155,12 @@
|
|
|
116
155
|
input$name="email"
|
|
117
156
|
input$autocapitalize="off"
|
|
118
157
|
input$spellcheck="false"
|
|
119
|
-
{...prefixFilter(
|
|
158
|
+
{...prefixFilter(restProps, 'email$')}
|
|
120
159
|
/>
|
|
121
160
|
</div>
|
|
122
161
|
{/if}
|
|
123
162
|
|
|
124
|
-
{#if clientConfig.regFields.
|
|
163
|
+
{#if $clientConfig.regFields.includes('phone')}
|
|
125
164
|
<div>
|
|
126
165
|
<Textfield
|
|
127
166
|
bind:value={phone}
|
|
@@ -130,13 +169,13 @@
|
|
|
130
169
|
style="width: 100%;"
|
|
131
170
|
input$autocomplete="tel"
|
|
132
171
|
input$name="phone"
|
|
133
|
-
{...prefixFilter(
|
|
172
|
+
{...prefixFilter(restProps, 'phone$')}
|
|
134
173
|
/>
|
|
135
174
|
</div>
|
|
136
175
|
{/if}
|
|
137
176
|
{/if}
|
|
138
177
|
|
|
139
|
-
|
|
178
|
+
{@render additional?.()}
|
|
140
179
|
|
|
141
180
|
{#if failureMessage}
|
|
142
181
|
<div class="tilmeld-login-failure">
|
|
@@ -149,8 +188,8 @@
|
|
|
149
188
|
<Button
|
|
150
189
|
variant="raised"
|
|
151
190
|
type="submit"
|
|
152
|
-
|
|
153
|
-
{...prefixFilter(
|
|
191
|
+
onclick={login}
|
|
192
|
+
{...prefixFilter(restProps, 'loginButton$')}
|
|
154
193
|
>
|
|
155
194
|
<Label>Log In</Label>
|
|
156
195
|
</Button>
|
|
@@ -158,29 +197,29 @@
|
|
|
158
197
|
<Button
|
|
159
198
|
variant="raised"
|
|
160
199
|
type="submit"
|
|
161
|
-
|
|
162
|
-
{...prefixFilter(
|
|
200
|
+
onclick={register}
|
|
201
|
+
{...prefixFilter(restProps, 'registerButton$')}
|
|
163
202
|
>
|
|
164
203
|
<Label>Create Account</Label>
|
|
165
204
|
</Button>
|
|
166
205
|
{/if}
|
|
167
206
|
</div>
|
|
168
207
|
|
|
169
|
-
{#if clientConfig.allowRegistration && showExistingUserToggle}
|
|
208
|
+
{#if $clientConfig.allowRegistration && showExistingUserToggle}
|
|
170
209
|
<div class="tilmeld-login-action">
|
|
171
210
|
{#if mode === 'login'}
|
|
172
211
|
<a
|
|
173
|
-
href=
|
|
174
|
-
|
|
175
|
-
{...prefixFilter(
|
|
212
|
+
href={'javascript:void(0);'}
|
|
213
|
+
onclick={() => (mode = 'register')}
|
|
214
|
+
{...prefixFilter(restProps, 'registerLink$')}
|
|
176
215
|
>
|
|
177
216
|
Create an account.
|
|
178
217
|
</a>
|
|
179
218
|
{:else}
|
|
180
219
|
<a
|
|
181
|
-
href=
|
|
182
|
-
|
|
183
|
-
{...prefixFilter(
|
|
220
|
+
href={'javascript:void(0);'}
|
|
221
|
+
onclick={() => (mode = 'login')}
|
|
222
|
+
{...prefixFilter(restProps, 'loginLink$')}
|
|
184
223
|
>
|
|
185
224
|
Log in to your account.
|
|
186
225
|
</a>
|
|
@@ -188,12 +227,12 @@
|
|
|
188
227
|
</div>
|
|
189
228
|
{/if}
|
|
190
229
|
|
|
191
|
-
{#if !hideRecovery && clientConfig.pwRecovery && mode === 'login'}
|
|
230
|
+
{#if !hideRecovery && $clientConfig.userFields.includes('email') && $clientConfig.pwRecovery && mode === 'login'}
|
|
192
231
|
<div class="tilmeld-login-action">
|
|
193
232
|
<a
|
|
194
|
-
href=
|
|
195
|
-
|
|
196
|
-
{...prefixFilter(
|
|
233
|
+
href={'javascript:void(0);'}
|
|
234
|
+
onclick={() => (recoverOpen = true)}
|
|
235
|
+
{...prefixFilter(restProps, 'recoverLink$')}
|
|
197
236
|
>
|
|
198
237
|
I can't access my account.
|
|
199
238
|
</a>
|
|
@@ -202,7 +241,7 @@
|
|
|
202
241
|
{User}
|
|
203
242
|
bind:open={recoverOpen}
|
|
204
243
|
bind:clientConfig
|
|
205
|
-
{...prefixFilter(
|
|
244
|
+
{...prefixFilter(restProps, 'recover$')}
|
|
206
245
|
/>
|
|
207
246
|
{/if}
|
|
208
247
|
</form>
|
|
@@ -210,95 +249,253 @@
|
|
|
210
249
|
</div>
|
|
211
250
|
|
|
212
251
|
<script lang="ts">
|
|
213
|
-
import {
|
|
214
|
-
import {
|
|
252
|
+
import type { ComponentProps, Snippet } from 'svelte';
|
|
253
|
+
import { onMount } from 'svelte';
|
|
254
|
+
import type { Writable } from 'svelte/store';
|
|
255
|
+
import { writable } from 'svelte/store';
|
|
215
256
|
import CircularProgress from '@smui/circular-progress';
|
|
216
257
|
import Button, { Label } from '@smui/button';
|
|
217
258
|
import Textfield from '@smui/textfield';
|
|
218
259
|
import HelperText from '@smui/textfield/helper-text';
|
|
219
260
|
import type { ActionArray } from '@smui/common/internal';
|
|
220
|
-
import {
|
|
221
|
-
|
|
222
|
-
exclude,
|
|
223
|
-
prefixFilter,
|
|
224
|
-
useActions,
|
|
225
|
-
} from '@smui/common/internal';
|
|
261
|
+
import { exclude, prefixFilter, useActions } from '@smui/common/internal';
|
|
262
|
+
import type { SmuiElementPropMap } from '@smui/common';
|
|
226
263
|
import type {
|
|
227
264
|
User as UserClass,
|
|
228
265
|
ClientConfig,
|
|
266
|
+
UserData,
|
|
267
|
+
CurrentUserData,
|
|
229
268
|
} from '@nymphjs/tilmeld-client';
|
|
230
269
|
import {
|
|
231
270
|
login as loginAction,
|
|
232
271
|
register as registerAction,
|
|
233
272
|
checkUsername as checkUsernameAction,
|
|
273
|
+
NeedTOTPError,
|
|
234
274
|
} from '@nymphjs/tilmeld-client';
|
|
235
275
|
import Recover from './Recover.svelte';
|
|
236
276
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
277
|
+
type OwnProps = {
|
|
278
|
+
/**
|
|
279
|
+
* An array of Action or [Action, ActionProps] to be applied to the element.
|
|
280
|
+
*/
|
|
281
|
+
use?: ActionArray;
|
|
282
|
+
/**
|
|
283
|
+
* A list of CSS styles.
|
|
284
|
+
*/
|
|
285
|
+
style?: string;
|
|
286
|
+
/**
|
|
287
|
+
* A writable store of the Nymph client config.
|
|
288
|
+
*
|
|
289
|
+
* It will be retrieved from the server if not provided.
|
|
290
|
+
*/
|
|
291
|
+
clientConfig?: Writable<ClientConfig | undefined>;
|
|
292
|
+
/**
|
|
293
|
+
* The User class from Nymph.
|
|
294
|
+
*/
|
|
295
|
+
User: typeof UserClass;
|
|
296
|
+
/**
|
|
297
|
+
* Hide the recovery link that only appears if password recovery is on.
|
|
298
|
+
*/
|
|
299
|
+
hideRecovery?: boolean;
|
|
300
|
+
/**
|
|
301
|
+
* Give focus to the username box (or email box) when the form is ready.
|
|
302
|
+
*/
|
|
303
|
+
autofocus?: boolean;
|
|
304
|
+
/**
|
|
305
|
+
* This determines whether the 'Log In' or 'Sign Up' button is activated
|
|
306
|
+
* and which corresponding form is shown.
|
|
307
|
+
*/
|
|
308
|
+
mode?: 'login' | 'register';
|
|
309
|
+
/**
|
|
310
|
+
* Whether to show the 'Log In'/'Sign Up' switcher buttons.
|
|
311
|
+
*/
|
|
312
|
+
showExistingUserToggle?: boolean;
|
|
313
|
+
/**
|
|
314
|
+
* The width of the form.
|
|
315
|
+
*/
|
|
316
|
+
width?: string;
|
|
317
|
+
/**
|
|
318
|
+
* User provided. You can bind to it if you need to.
|
|
319
|
+
*/
|
|
320
|
+
username?: string;
|
|
321
|
+
/**
|
|
322
|
+
* User provided. You can bind to it if you need to.
|
|
323
|
+
*/
|
|
324
|
+
password?: string;
|
|
325
|
+
/**
|
|
326
|
+
* User provided. You can bind to it if you need to.
|
|
327
|
+
*/
|
|
328
|
+
password2?: string;
|
|
329
|
+
/**
|
|
330
|
+
* User provided. You can bind to it if you need to.
|
|
331
|
+
*/
|
|
332
|
+
name?: string;
|
|
333
|
+
/**
|
|
334
|
+
* User provided. You can bind to it if you need to.
|
|
335
|
+
*/
|
|
336
|
+
email?: string;
|
|
337
|
+
/**
|
|
338
|
+
* User provided. You can bind to it if you need to.
|
|
339
|
+
*/
|
|
340
|
+
phone?: string;
|
|
341
|
+
/**
|
|
342
|
+
* User provided. You can bind to it if you need to.
|
|
343
|
+
*/
|
|
344
|
+
code?: string;
|
|
345
|
+
/**
|
|
346
|
+
* Provide this for anything else that should go up to the server.
|
|
347
|
+
*
|
|
348
|
+
* You can look for it in the User events.
|
|
349
|
+
*/
|
|
350
|
+
additionalData?: { [k: string]: any };
|
|
255
351
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
/** User provided. You can bind to it if you need to. */
|
|
261
|
-
export let password2 = '';
|
|
262
|
-
/** User provided. You can bind to it if you need to. */
|
|
263
|
-
export let name = '';
|
|
264
|
-
/** User provided. You can bind to it if you need to. */
|
|
265
|
-
export let email = '';
|
|
266
|
-
/** User provided. You can bind to it if you need to. */
|
|
267
|
-
export let phone = '';
|
|
352
|
+
/**
|
|
353
|
+
* A spot for additional content.
|
|
354
|
+
*/
|
|
355
|
+
additional?: Snippet;
|
|
268
356
|
|
|
269
|
-
|
|
270
|
-
|
|
357
|
+
/**
|
|
358
|
+
* A callback to be called when a user is registered.
|
|
359
|
+
*/
|
|
360
|
+
onregister?: (details: {
|
|
361
|
+
user: UserClass & UserData & CurrentUserData;
|
|
362
|
+
}) => void;
|
|
363
|
+
/**
|
|
364
|
+
* A callback to be called when a user is logged in.
|
|
365
|
+
*/
|
|
366
|
+
onlogin?: (details: {
|
|
367
|
+
user: UserClass & UserData & CurrentUserData;
|
|
368
|
+
}) => void;
|
|
369
|
+
};
|
|
370
|
+
let {
|
|
371
|
+
use = [],
|
|
372
|
+
style = '',
|
|
373
|
+
clientConfig = $bindable(writable(false as unknown as undefined)),
|
|
374
|
+
User,
|
|
375
|
+
hideRecovery = false,
|
|
376
|
+
autofocus = true,
|
|
377
|
+
mode = $bindable('login'),
|
|
378
|
+
showExistingUserToggle = true,
|
|
379
|
+
width = '220px',
|
|
380
|
+
username = $bindable(''),
|
|
381
|
+
password = $bindable(''),
|
|
382
|
+
password2 = $bindable(''),
|
|
383
|
+
name = $bindable(''),
|
|
384
|
+
email = $bindable(''),
|
|
385
|
+
phone = $bindable(''),
|
|
386
|
+
code = $bindable(''),
|
|
387
|
+
additionalData,
|
|
388
|
+
additional,
|
|
389
|
+
onregister,
|
|
390
|
+
onlogin,
|
|
391
|
+
...restProps
|
|
392
|
+
}: OwnProps & {
|
|
393
|
+
[
|
|
394
|
+
k in keyof SmuiElementPropMap['div'] as `successRegisteredMessage\$${k}`
|
|
395
|
+
]?: SmuiElementPropMap['div'][k];
|
|
396
|
+
} & {
|
|
397
|
+
[
|
|
398
|
+
k in keyof SmuiElementPropMap['div'] as `successLoginMessage\$${k}`
|
|
399
|
+
]?: SmuiElementPropMap['div'][k];
|
|
400
|
+
} & {
|
|
401
|
+
[
|
|
402
|
+
k in keyof ComponentProps<typeof Textfield> as `username\$${k}`
|
|
403
|
+
]?: ComponentProps<typeof Textfield>[k];
|
|
404
|
+
} & {
|
|
405
|
+
[
|
|
406
|
+
k in keyof ComponentProps<typeof Textfield> as `password\$${k}`
|
|
407
|
+
]?: ComponentProps<typeof Textfield>[k];
|
|
408
|
+
} & {
|
|
409
|
+
[
|
|
410
|
+
k in keyof ComponentProps<typeof Textfield> as `password2\$${k}`
|
|
411
|
+
]?: ComponentProps<typeof Textfield>[k];
|
|
412
|
+
} & {
|
|
413
|
+
[
|
|
414
|
+
k in keyof ComponentProps<typeof Textfield> as `name\$${k}`
|
|
415
|
+
]?: ComponentProps<typeof Textfield>[k];
|
|
416
|
+
} & {
|
|
417
|
+
[
|
|
418
|
+
k in keyof ComponentProps<typeof Textfield> as `email\$${k}`
|
|
419
|
+
]?: ComponentProps<typeof Textfield>[k];
|
|
420
|
+
} & {
|
|
421
|
+
[
|
|
422
|
+
k in keyof ComponentProps<typeof Textfield> as `phone\$${k}`
|
|
423
|
+
]?: ComponentProps<typeof Textfield>[k];
|
|
424
|
+
} & {
|
|
425
|
+
[
|
|
426
|
+
k in keyof ComponentProps<typeof Textfield> as `code\$${k}`
|
|
427
|
+
]?: ComponentProps<typeof Textfield>[k];
|
|
428
|
+
} & {
|
|
429
|
+
[
|
|
430
|
+
k in keyof SmuiElementPropMap['div'] as `codeMessage\$${k}`
|
|
431
|
+
]?: SmuiElementPropMap['div'][k];
|
|
432
|
+
} & {
|
|
433
|
+
[
|
|
434
|
+
k in keyof ComponentProps<typeof CircularProgress> as `progress\$${k}`
|
|
435
|
+
]?: ComponentProps<typeof CircularProgress>[k];
|
|
436
|
+
} & {
|
|
437
|
+
[
|
|
438
|
+
k in keyof ComponentProps<
|
|
439
|
+
typeof Button<undefined, 'button'>
|
|
440
|
+
> as `loginButton\$${k}`
|
|
441
|
+
]?: ComponentProps<typeof Button<undefined, 'button'>>[k];
|
|
442
|
+
} & {
|
|
443
|
+
[
|
|
444
|
+
k in keyof ComponentProps<
|
|
445
|
+
typeof Button<undefined, 'button'>
|
|
446
|
+
> as `registerButton\$${k}`
|
|
447
|
+
]?: ComponentProps<typeof Button<undefined, 'button'>>[k];
|
|
448
|
+
} & {
|
|
449
|
+
[
|
|
450
|
+
k in keyof SmuiElementPropMap['a'] as `registerLink\$${k}`
|
|
451
|
+
]?: SmuiElementPropMap['a'][k];
|
|
452
|
+
} & {
|
|
453
|
+
[
|
|
454
|
+
k in keyof SmuiElementPropMap['a'] as `loginLink\$${k}`
|
|
455
|
+
]?: SmuiElementPropMap['a'][k];
|
|
456
|
+
} & {
|
|
457
|
+
[
|
|
458
|
+
k in keyof SmuiElementPropMap['a'] as `recoverLink\$${k}`
|
|
459
|
+
]?: SmuiElementPropMap['a'][k];
|
|
460
|
+
} & {
|
|
461
|
+
[
|
|
462
|
+
k in keyof ComponentProps<typeof Recover> as `recover\$${k}`
|
|
463
|
+
]?: ComponentProps<typeof Recover>[k];
|
|
464
|
+
} = $props();
|
|
271
465
|
|
|
272
|
-
let usernameElem: Textfield;
|
|
273
|
-
let successLoginMessage: string | undefined =
|
|
274
|
-
let successRegisteredMessage: string | undefined =
|
|
275
|
-
let failureMessage: string | undefined =
|
|
276
|
-
let usernameTimer:
|
|
277
|
-
let usernameVerified: boolean | undefined =
|
|
278
|
-
let usernameVerifiedMessage: string | undefined =
|
|
279
|
-
let
|
|
280
|
-
let
|
|
281
|
-
let recoverOpen = false;
|
|
466
|
+
let usernameElem: Textfield | undefined = $state();
|
|
467
|
+
let successLoginMessage: string | undefined = $state();
|
|
468
|
+
let successRegisteredMessage: string | undefined = $state();
|
|
469
|
+
let failureMessage: string | undefined = $state();
|
|
470
|
+
let usernameTimer: number | undefined = undefined;
|
|
471
|
+
let usernameVerified: boolean | undefined = $state();
|
|
472
|
+
let usernameVerifiedMessage: string | undefined = $state();
|
|
473
|
+
let totp = $state(false);
|
|
474
|
+
let loading = $state(false);
|
|
475
|
+
let recoverOpen = $state(false);
|
|
282
476
|
|
|
283
|
-
|
|
284
|
-
|
|
477
|
+
const nameFirst = $derived(name?.match(/^(.*?)(?: ([^ ]+))?$/)?.[1] ?? '');
|
|
478
|
+
const nameLast = $derived(name?.match(/^(.*?)(?: ([^ ]+))?$/)?.[2] ?? '');
|
|
285
479
|
|
|
286
480
|
let _previousMode = mode;
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
481
|
+
$effect(() => {
|
|
482
|
+
if (mode !== _previousMode) {
|
|
483
|
+
failureMessage = '';
|
|
484
|
+
checkUsername(username);
|
|
485
|
+
_previousMode = mode;
|
|
486
|
+
}
|
|
487
|
+
});
|
|
292
488
|
|
|
293
|
-
|
|
489
|
+
$effect(() => {
|
|
294
490
|
checkUsername(username);
|
|
295
|
-
}
|
|
491
|
+
});
|
|
296
492
|
|
|
297
493
|
onMount(async () => {
|
|
298
|
-
if (clientConfig === undefined) {
|
|
299
|
-
clientConfig =
|
|
494
|
+
if ($clientConfig === (false as unknown as undefined)) {
|
|
495
|
+
$clientConfig = undefined;
|
|
496
|
+
$clientConfig = await User.getClientConfig();
|
|
300
497
|
}
|
|
301
|
-
if (
|
|
498
|
+
if (!$clientConfig.allowRegistration) {
|
|
302
499
|
mode = 'login';
|
|
303
500
|
}
|
|
304
501
|
if (autofocus && usernameElem) {
|
|
@@ -309,22 +506,34 @@
|
|
|
309
506
|
async function login() {
|
|
310
507
|
successLoginMessage = undefined;
|
|
311
508
|
failureMessage = undefined;
|
|
312
|
-
|
|
509
|
+
loading = true;
|
|
313
510
|
try {
|
|
314
|
-
const data = await loginAction(
|
|
511
|
+
const data = await loginAction(
|
|
512
|
+
User,
|
|
513
|
+
username,
|
|
514
|
+
password,
|
|
515
|
+
code,
|
|
516
|
+
additionalData,
|
|
517
|
+
);
|
|
315
518
|
successLoginMessage = data.message;
|
|
316
|
-
|
|
519
|
+
if (data.user) {
|
|
520
|
+
onlogin?.({ user: data.user });
|
|
521
|
+
}
|
|
317
522
|
} catch (e: any) {
|
|
318
|
-
|
|
523
|
+
if (e instanceof NeedTOTPError) {
|
|
524
|
+
totp = true;
|
|
525
|
+
} else {
|
|
526
|
+
failureMessage = e?.message;
|
|
527
|
+
}
|
|
319
528
|
}
|
|
320
|
-
|
|
529
|
+
loading = false;
|
|
321
530
|
}
|
|
322
531
|
|
|
323
532
|
async function register() {
|
|
324
533
|
successRegisteredMessage = undefined;
|
|
325
534
|
successLoginMessage = undefined;
|
|
326
535
|
failureMessage = undefined;
|
|
327
|
-
|
|
536
|
+
loading = true;
|
|
328
537
|
try {
|
|
329
538
|
const data = await registerAction(User, {
|
|
330
539
|
username,
|
|
@@ -338,15 +547,19 @@
|
|
|
338
547
|
...(additionalData ? { additionalData } : {}),
|
|
339
548
|
});
|
|
340
549
|
successRegisteredMessage = data.message;
|
|
341
|
-
|
|
550
|
+
if (data.user) {
|
|
551
|
+
onregister?.({ user: data.user });
|
|
552
|
+
}
|
|
342
553
|
if (data.loggedin) {
|
|
343
554
|
successLoginMessage = data.message;
|
|
344
|
-
|
|
555
|
+
if (data.user) {
|
|
556
|
+
onlogin?.({ user: data.user });
|
|
557
|
+
}
|
|
345
558
|
}
|
|
346
559
|
} catch (e: any) {
|
|
347
560
|
failureMessage = e?.message;
|
|
348
561
|
}
|
|
349
|
-
|
|
562
|
+
loading = false;
|
|
350
563
|
}
|
|
351
564
|
|
|
352
565
|
function checkUsername(newValue: string) {
|