@nymphjs/tilmeld-components 1.0.0-beta.80 → 1.0.0-beta.82
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 +10 -0
- package/README.md +1 -1
- package/dist/Account.svelte +515 -0
- package/dist/ChangePassword.svelte +309 -0
- package/dist/Login.svelte +591 -0
- package/dist/Recover.svelte +457 -0
- package/dist/RevokeTokens.svelte +236 -0
- package/{lib → dist}/TwoFactor.svelte +250 -147
- package/jest.config.js +15 -0
- package/package.json +36 -32
- package/src/Account.svelte +212 -100
- package/src/ChangePassword.svelte +130 -53
- package/src/Login.svelte +264 -112
- package/src/Recover.svelte +168 -70
- package/src/RevokeTokens.svelte +103 -44
- package/src/TwoFactor.svelte +147 -78
- package/tsconfig.json +6 -3
- package/dist/index.cjs +0 -3
- package/dist/index.cjs.LICENSE.txt +0 -114
- package/dist/index.cjs.map +0 -1
- package/jest.config.cjs +0 -6
- package/lib/Account.svelte +0 -387
- package/lib/Account.svelte.map +0 -1
- package/lib/ChangePassword.svelte +0 -212
- package/lib/ChangePassword.svelte.map +0 -1
- package/lib/Login.svelte +0 -408
- package/lib/Login.svelte.map +0 -1
- package/lib/Recover.svelte +0 -342
- package/lib/Recover.svelte.map +0 -1
- package/lib/RevokeTokens.svelte +0 -156
- package/lib/RevokeTokens.svelte.map +0 -1
- package/lib/TwoFactor.svelte.map +0 -1
- package/webpack.config.cjs +0 -36
- /package/{lib → dist}/index.d.ts +0 -0
- /package/{lib → dist}/index.js +0 -0
- /package/{lib → dist}/index.js.map +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,16 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
# [1.0.0-beta.82](https://github.com/sciactive/nymphjs/compare/v1.0.0-beta.81...v1.0.0-beta.82) (2024-12-15)
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
- migrate to es modules, upgrade all packages, migrate to Svelte 5 ([3f2b9e5](https://github.com/sciactive/nymphjs/commit/3f2b9e517b39934eddce66601d7fc747fbf3f9e6))
|
|
11
|
+
|
|
12
|
+
# [1.0.0-beta.81](https://github.com/sciactive/nymphjs/compare/v1.0.0-beta.80...v1.0.0-beta.81) (2024-09-28)
|
|
13
|
+
|
|
14
|
+
**Note:** Version bump only for package @nymphjs/tilmeld-components
|
|
15
|
+
|
|
6
16
|
# [1.0.0-beta.80](https://github.com/sciactive/nymphjs/compare/v1.0.0-beta.79...v1.0.0-beta.80) (2024-09-28)
|
|
7
17
|
|
|
8
18
|
**Note:** Version bump only for package @nymphjs/tilmeld-components
|
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@ You need to have an SMUI theme compiled and installed on your front end app. If
|
|
|
16
16
|
|
|
17
17
|
# License
|
|
18
18
|
|
|
19
|
-
Copyright 2021 SciActive Inc
|
|
19
|
+
Copyright 2021-2024 SciActive Inc
|
|
20
20
|
|
|
21
21
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
22
22
|
you may not use this file except in compliance with the License.
|
|
@@ -0,0 +1,515 @@
|
|
|
1
|
+
<svelte:options runes />
|
|
2
|
+
|
|
3
|
+
{#if $clientConfig != null && $user != null}
|
|
4
|
+
<Dialog
|
|
5
|
+
{use}
|
|
6
|
+
bind:open
|
|
7
|
+
aria-labelledby="tilmeld-account-title"
|
|
8
|
+
aria-describedby="tilmeld-account-content"
|
|
9
|
+
surface$class="tilmeld-account-dialog-surface"
|
|
10
|
+
{...exclude(restProps, [
|
|
11
|
+
'username$',
|
|
12
|
+
'email$',
|
|
13
|
+
'nameFirst$',
|
|
14
|
+
'nameMiddle$',
|
|
15
|
+
'nameLast$',
|
|
16
|
+
'phone$',
|
|
17
|
+
'changePasswordLink$',
|
|
18
|
+
'revokeTokensLink$',
|
|
19
|
+
'closeButton$',
|
|
20
|
+
'saveButton$',
|
|
21
|
+
'changePassword$',
|
|
22
|
+
'revokeTokens$',
|
|
23
|
+
'twoFactor$',
|
|
24
|
+
'progress$',
|
|
25
|
+
])}
|
|
26
|
+
>
|
|
27
|
+
<!-- Title cannot contain leading whitespace due to mdc-typography-baseline-top() -->
|
|
28
|
+
<Title id="tilmeld-account-title">{title}</Title>
|
|
29
|
+
<Content id="tilmeld-account-content">
|
|
30
|
+
{#if !$clientConfig.emailUsernames && $clientConfig.allowUsernameChange}
|
|
31
|
+
<div>
|
|
32
|
+
<Textfield
|
|
33
|
+
bind:value={$user.username}
|
|
34
|
+
label="Username"
|
|
35
|
+
type="text"
|
|
36
|
+
style="width: 100%;"
|
|
37
|
+
helperLine$style="width: 100%;"
|
|
38
|
+
invalid={usernameVerified === false}
|
|
39
|
+
input$autocomplete="username"
|
|
40
|
+
input$autocapitalize="off"
|
|
41
|
+
input$spellcheck="false"
|
|
42
|
+
input$emptyValueUndefined
|
|
43
|
+
{...prefixFilter(restProps, 'username$')}
|
|
44
|
+
>
|
|
45
|
+
{#snippet helper()}
|
|
46
|
+
<HelperText persistent>
|
|
47
|
+
{usernameVerifiedMessage || ''}
|
|
48
|
+
</HelperText>
|
|
49
|
+
{/snippet}
|
|
50
|
+
</Textfield>
|
|
51
|
+
</div>
|
|
52
|
+
{/if}
|
|
53
|
+
|
|
54
|
+
{#if $clientConfig.emailUsernames || $clientConfig.userFields.includes('email')}
|
|
55
|
+
<div>
|
|
56
|
+
<Textfield
|
|
57
|
+
bind:value={$user.email}
|
|
58
|
+
label="Email"
|
|
59
|
+
type="email"
|
|
60
|
+
style="width: 100%;"
|
|
61
|
+
helperLine$style="width: 100%;"
|
|
62
|
+
invalid={emailVerified === false}
|
|
63
|
+
input$autocomplete="email"
|
|
64
|
+
input$autocapitalize="off"
|
|
65
|
+
input$spellcheck="false"
|
|
66
|
+
input$emptyValueUndefined
|
|
67
|
+
{...prefixFilter(restProps, 'email$')}
|
|
68
|
+
>
|
|
69
|
+
{#snippet helper()}
|
|
70
|
+
<HelperText persistent>
|
|
71
|
+
{emailVerifiedMessage || ''}
|
|
72
|
+
</HelperText>
|
|
73
|
+
{/snippet}
|
|
74
|
+
</Textfield>
|
|
75
|
+
</div>
|
|
76
|
+
{/if}
|
|
77
|
+
|
|
78
|
+
{#if $clientConfig.userFields.includes('name')}
|
|
79
|
+
<div>
|
|
80
|
+
<Textfield
|
|
81
|
+
bind:value={$user.nameFirst}
|
|
82
|
+
label="First Name"
|
|
83
|
+
type="text"
|
|
84
|
+
style="width: 100%;"
|
|
85
|
+
input$autocomplete="given-name"
|
|
86
|
+
input$emptyValueUndefined
|
|
87
|
+
{...prefixFilter(restProps, 'nameFirst$')}
|
|
88
|
+
/>
|
|
89
|
+
</div>
|
|
90
|
+
|
|
91
|
+
<div>
|
|
92
|
+
<Textfield
|
|
93
|
+
bind:value={$user.nameMiddle}
|
|
94
|
+
label="Middle Name"
|
|
95
|
+
type="text"
|
|
96
|
+
style="width: 100%;"
|
|
97
|
+
input$autocomplete="additional-name"
|
|
98
|
+
input$emptyValueUndefined
|
|
99
|
+
{...prefixFilter(restProps, 'nameMiddle$')}
|
|
100
|
+
/>
|
|
101
|
+
</div>
|
|
102
|
+
|
|
103
|
+
<div>
|
|
104
|
+
<Textfield
|
|
105
|
+
bind:value={$user.nameLast}
|
|
106
|
+
label="Last Name"
|
|
107
|
+
type="text"
|
|
108
|
+
style="width: 100%;"
|
|
109
|
+
input$autocomplete="family-name"
|
|
110
|
+
input$emptyValueUndefined
|
|
111
|
+
{...prefixFilter(restProps, 'nameLast$')}
|
|
112
|
+
/>
|
|
113
|
+
</div>
|
|
114
|
+
{/if}
|
|
115
|
+
|
|
116
|
+
{#if $clientConfig.userFields.includes('phone')}
|
|
117
|
+
<div>
|
|
118
|
+
<Textfield
|
|
119
|
+
bind:value={$user.phone}
|
|
120
|
+
label="Phone Number"
|
|
121
|
+
type="tel"
|
|
122
|
+
style="width: 100%;"
|
|
123
|
+
input$autocomplete="tel"
|
|
124
|
+
input$name="phone"
|
|
125
|
+
input$emptyValueUndefined
|
|
126
|
+
{...prefixFilter(restProps, 'phone$')}
|
|
127
|
+
/>
|
|
128
|
+
</div>
|
|
129
|
+
{/if}
|
|
130
|
+
|
|
131
|
+
{@render additional?.()}
|
|
132
|
+
|
|
133
|
+
<div class="tilmeld-account-action">
|
|
134
|
+
<a
|
|
135
|
+
href={'javascript:void(0);'}
|
|
136
|
+
onclick={() => {
|
|
137
|
+
open = false;
|
|
138
|
+
changePasswordOpen = true;
|
|
139
|
+
}}
|
|
140
|
+
{...prefixFilter(restProps, 'changePasswordLink$')}
|
|
141
|
+
>
|
|
142
|
+
Change your password.
|
|
143
|
+
</a>
|
|
144
|
+
</div>
|
|
145
|
+
|
|
146
|
+
<div class="tilmeld-account-action">
|
|
147
|
+
<a
|
|
148
|
+
href={'javascript:void(0);'}
|
|
149
|
+
onclick={() => {
|
|
150
|
+
open = false;
|
|
151
|
+
revokeTokensOpen = true;
|
|
152
|
+
}}
|
|
153
|
+
{...prefixFilter(restProps, 'revokeTokensLink$')}
|
|
154
|
+
>
|
|
155
|
+
Log out of other sessions.
|
|
156
|
+
</a>
|
|
157
|
+
|
|
158
|
+
<div class="tilmeld-account-action">
|
|
159
|
+
<a
|
|
160
|
+
href={'javascript:void(0);'}
|
|
161
|
+
onclick={() => {
|
|
162
|
+
open = false;
|
|
163
|
+
twoFactorOpen = true;
|
|
164
|
+
}}
|
|
165
|
+
{...prefixFilter(restProps, 'twoFactor$')}
|
|
166
|
+
>
|
|
167
|
+
{#if hasTOTPSecret === false}
|
|
168
|
+
Enable two factor authentication (2FA).
|
|
169
|
+
{:else}
|
|
170
|
+
Manage two factor authentication (2FA).
|
|
171
|
+
{/if}
|
|
172
|
+
</a>
|
|
173
|
+
</div>
|
|
174
|
+
|
|
175
|
+
{#if failureMessage}
|
|
176
|
+
<div class="tilmeld-account-failure">
|
|
177
|
+
{failureMessage}
|
|
178
|
+
</div>
|
|
179
|
+
{/if}
|
|
180
|
+
|
|
181
|
+
{#if loading}
|
|
182
|
+
<div class="tilmeld-account-loading">
|
|
183
|
+
<CircularProgress
|
|
184
|
+
style="height: 24px; width: 24px;"
|
|
185
|
+
indeterminate
|
|
186
|
+
{...prefixFilter(restProps, 'progress$')}
|
|
187
|
+
/>
|
|
188
|
+
</div>
|
|
189
|
+
{/if}
|
|
190
|
+
</div>
|
|
191
|
+
</Content>
|
|
192
|
+
<Actions>
|
|
193
|
+
<Button disabled={loading} {...prefixFilter(restProps, 'closeButton$')}>
|
|
194
|
+
<Label>Close</Label>
|
|
195
|
+
</Button>
|
|
196
|
+
<Button
|
|
197
|
+
onclick={preventDefault(stopPropagation(save))}
|
|
198
|
+
disabled={loading}
|
|
199
|
+
{...prefixFilter(restProps, 'saveButton$')}
|
|
200
|
+
>
|
|
201
|
+
<Label>Save Changes</Label>
|
|
202
|
+
</Button>
|
|
203
|
+
</Actions>
|
|
204
|
+
</Dialog>
|
|
205
|
+
|
|
206
|
+
<ChangePassword
|
|
207
|
+
{User}
|
|
208
|
+
bind:open={changePasswordOpen}
|
|
209
|
+
bind:user
|
|
210
|
+
{...prefixFilter(restProps, 'changePassword$')}
|
|
211
|
+
/>
|
|
212
|
+
|
|
213
|
+
<RevokeTokens
|
|
214
|
+
{User}
|
|
215
|
+
bind:open={revokeTokensOpen}
|
|
216
|
+
bind:user
|
|
217
|
+
{...prefixFilter(restProps, 'revokeTokens$')}
|
|
218
|
+
/>
|
|
219
|
+
|
|
220
|
+
<TwoFactor
|
|
221
|
+
{User}
|
|
222
|
+
bind:open={twoFactorOpen}
|
|
223
|
+
bind:user
|
|
224
|
+
bind:hasTOTPSecret
|
|
225
|
+
{...prefixFilter(restProps, 'twoFactor$')}
|
|
226
|
+
/>
|
|
227
|
+
{/if}
|
|
228
|
+
|
|
229
|
+
<script lang="ts">
|
|
230
|
+
import type { ComponentProps, Snippet } from 'svelte';
|
|
231
|
+
import { onMount, onDestroy } from 'svelte';
|
|
232
|
+
import type { Writable } from 'svelte/store';
|
|
233
|
+
import { writable } from 'svelte/store';
|
|
234
|
+
import CircularProgress from '@smui/circular-progress';
|
|
235
|
+
import Dialog, { Title, Content, Actions } from '@smui/dialog';
|
|
236
|
+
import Textfield from '@smui/textfield';
|
|
237
|
+
import HelperText from '@smui/textfield/helper-text';
|
|
238
|
+
import Button, { Label } from '@smui/button';
|
|
239
|
+
import type { ActionArray } from '@smui/common/internal';
|
|
240
|
+
import { exclude, prefixFilter } from '@smui/common/internal';
|
|
241
|
+
import { preventDefault, stopPropagation } from '@smui/common/events';
|
|
242
|
+
import type { SmuiElementPropMap } from '@smui/common';
|
|
243
|
+
import type { ClientConfig, CurrentUserData } from '@nymphjs/tilmeld-client';
|
|
244
|
+
import type { User as UserClass } from '@nymphjs/tilmeld-client';
|
|
245
|
+
import ChangePassword from './ChangePassword.svelte';
|
|
246
|
+
import RevokeTokens from './RevokeTokens.svelte';
|
|
247
|
+
import TwoFactor from './TwoFactor.svelte';
|
|
248
|
+
|
|
249
|
+
type OwnProps = {
|
|
250
|
+
/**
|
|
251
|
+
* An array of Action or [Action, ActionProps] to be applied to the element.
|
|
252
|
+
*/
|
|
253
|
+
use?: ActionArray;
|
|
254
|
+
/**
|
|
255
|
+
* Whether the dialog is open.
|
|
256
|
+
*/
|
|
257
|
+
open?: boolean;
|
|
258
|
+
/**
|
|
259
|
+
* The title of the dialog.
|
|
260
|
+
*/
|
|
261
|
+
title?: string;
|
|
262
|
+
/**
|
|
263
|
+
* A writable store of the Nymph client config.
|
|
264
|
+
*
|
|
265
|
+
* It will be retrieved from the server if not provided.
|
|
266
|
+
*/
|
|
267
|
+
clientConfig?: Writable<ClientConfig | undefined>;
|
|
268
|
+
/**
|
|
269
|
+
* The User class from Nymph.
|
|
270
|
+
*/
|
|
271
|
+
User: typeof UserClass;
|
|
272
|
+
/**
|
|
273
|
+
* A writable store of the current user.
|
|
274
|
+
*
|
|
275
|
+
* It will be retrieved from the server if not provided.
|
|
276
|
+
*/
|
|
277
|
+
user?: Writable<(UserClass & CurrentUserData) | null | undefined>;
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* A spot for additional content.
|
|
281
|
+
*/
|
|
282
|
+
additional?: Snippet;
|
|
283
|
+
};
|
|
284
|
+
let {
|
|
285
|
+
use = [],
|
|
286
|
+
open = $bindable(false),
|
|
287
|
+
title = 'Your Account',
|
|
288
|
+
clientConfig = $bindable(writable(false as unknown as undefined)),
|
|
289
|
+
User,
|
|
290
|
+
user = $bindable(writable(false as unknown as undefined)),
|
|
291
|
+
additional,
|
|
292
|
+
...restProps
|
|
293
|
+
}: OwnProps & {
|
|
294
|
+
[k in keyof ComponentProps<
|
|
295
|
+
typeof Textfield
|
|
296
|
+
> as `username\$${k}`]?: ComponentProps<typeof Textfield>[k];
|
|
297
|
+
} & {
|
|
298
|
+
[k in keyof ComponentProps<
|
|
299
|
+
typeof Textfield
|
|
300
|
+
> as `email\$${k}`]?: ComponentProps<typeof Textfield>[k];
|
|
301
|
+
} & {
|
|
302
|
+
[k in keyof ComponentProps<
|
|
303
|
+
typeof Textfield
|
|
304
|
+
> as `nameFirst\$${k}`]?: ComponentProps<typeof Textfield>[k];
|
|
305
|
+
} & {
|
|
306
|
+
[k in keyof ComponentProps<
|
|
307
|
+
typeof Textfield
|
|
308
|
+
> as `nameMiddle\$${k}`]?: ComponentProps<typeof Textfield>[k];
|
|
309
|
+
} & {
|
|
310
|
+
[k in keyof ComponentProps<
|
|
311
|
+
typeof Textfield
|
|
312
|
+
> as `nameLast\$${k}`]?: ComponentProps<typeof Textfield>[k];
|
|
313
|
+
} & {
|
|
314
|
+
[k in keyof ComponentProps<
|
|
315
|
+
typeof Textfield
|
|
316
|
+
> as `phone\$${k}`]?: ComponentProps<typeof Textfield>[k];
|
|
317
|
+
} & {
|
|
318
|
+
[k in keyof SmuiElementPropMap['a'] as `changePasswordLink\$${k}`]?: SmuiElementPropMap['a'][k];
|
|
319
|
+
} & {
|
|
320
|
+
[k in keyof SmuiElementPropMap['a'] as `revokeTokensLink\$${k}`]?: SmuiElementPropMap['a'][k];
|
|
321
|
+
} & {
|
|
322
|
+
[k in keyof ComponentProps<
|
|
323
|
+
typeof CircularProgress
|
|
324
|
+
> as `progress\$${k}`]?: ComponentProps<typeof CircularProgress>[k];
|
|
325
|
+
} & {
|
|
326
|
+
[k in keyof ComponentProps<
|
|
327
|
+
typeof Button<undefined, 'button'>
|
|
328
|
+
> as `closeButton\$${k}`]?: ComponentProps<
|
|
329
|
+
typeof Button<undefined, 'button'>
|
|
330
|
+
>[k];
|
|
331
|
+
} & {
|
|
332
|
+
[k in keyof ComponentProps<
|
|
333
|
+
typeof Button<undefined, 'button'>
|
|
334
|
+
> as `saveButton\$${k}`]?: ComponentProps<
|
|
335
|
+
typeof Button<undefined, 'button'>
|
|
336
|
+
>[k];
|
|
337
|
+
} & {
|
|
338
|
+
[k in keyof ComponentProps<
|
|
339
|
+
typeof ChangePassword
|
|
340
|
+
> as `changePassword\$${k}`]?: ComponentProps<typeof ChangePassword>[k];
|
|
341
|
+
} & {
|
|
342
|
+
[k in keyof ComponentProps<
|
|
343
|
+
typeof RevokeTokens
|
|
344
|
+
> as `revokeTokens\$${k}`]?: ComponentProps<typeof RevokeTokens>[k];
|
|
345
|
+
} & {
|
|
346
|
+
[k in keyof ComponentProps<
|
|
347
|
+
typeof TwoFactor
|
|
348
|
+
> as `twoFactor\$${k}`]?: ComponentProps<typeof TwoFactor>[k];
|
|
349
|
+
} = $props();
|
|
350
|
+
|
|
351
|
+
let loading = $state(false);
|
|
352
|
+
let originalUsername: string | undefined = $user?.username;
|
|
353
|
+
let originalEmail: string | undefined = $user?.email;
|
|
354
|
+
let failureMessage: string | undefined = $state();
|
|
355
|
+
let usernameTimer: NodeJS.Timeout | undefined = undefined;
|
|
356
|
+
let usernameVerified: boolean | undefined = $state();
|
|
357
|
+
let usernameVerifiedMessage: string | undefined = $state();
|
|
358
|
+
let emailTimer: NodeJS.Timeout | undefined = undefined;
|
|
359
|
+
let emailVerified: boolean | undefined = $state();
|
|
360
|
+
let emailVerifiedMessage: string | undefined = $state();
|
|
361
|
+
let hasTOTPSecret: boolean | null = $state(null);
|
|
362
|
+
let changePasswordOpen = $state(false);
|
|
363
|
+
let revokeTokensOpen = $state(false);
|
|
364
|
+
let twoFactorOpen = $state(false);
|
|
365
|
+
|
|
366
|
+
const onLogin = (currentUser: UserClass & CurrentUserData) => {
|
|
367
|
+
$user = currentUser;
|
|
368
|
+
originalUsername = $user?.username;
|
|
369
|
+
originalEmail = $user?.email;
|
|
370
|
+
};
|
|
371
|
+
const onLogout = () => {
|
|
372
|
+
$user = null;
|
|
373
|
+
originalUsername = undefined;
|
|
374
|
+
originalEmail = undefined;
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
$effect(() => {
|
|
378
|
+
if ($user && $user.username !== originalUsername) {
|
|
379
|
+
if ($user.$isDirty('username')) {
|
|
380
|
+
checkUsername();
|
|
381
|
+
} else {
|
|
382
|
+
originalUsername = $user.username;
|
|
383
|
+
}
|
|
384
|
+
} else {
|
|
385
|
+
if (usernameTimer) {
|
|
386
|
+
clearTimeout(usernameTimer);
|
|
387
|
+
}
|
|
388
|
+
usernameVerified = true;
|
|
389
|
+
usernameVerifiedMessage = undefined;
|
|
390
|
+
}
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
$effect(() => {
|
|
394
|
+
if ($user && $user.email !== originalEmail) {
|
|
395
|
+
if ($user.$isDirty('email')) {
|
|
396
|
+
checkEmail();
|
|
397
|
+
} else {
|
|
398
|
+
originalEmail = $user.email;
|
|
399
|
+
}
|
|
400
|
+
} else {
|
|
401
|
+
if (emailTimer) {
|
|
402
|
+
clearTimeout(emailTimer);
|
|
403
|
+
}
|
|
404
|
+
emailVerified = true;
|
|
405
|
+
emailVerifiedMessage = undefined;
|
|
406
|
+
}
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
onMount(async () => {
|
|
410
|
+
User.on('login', onLogin);
|
|
411
|
+
User.on('logout', onLogout);
|
|
412
|
+
if ($user === (false as unknown as undefined)) {
|
|
413
|
+
$user = undefined;
|
|
414
|
+
$user = await User.current();
|
|
415
|
+
}
|
|
416
|
+
originalUsername = $user?.username;
|
|
417
|
+
originalEmail = $user?.email;
|
|
418
|
+
});
|
|
419
|
+
onMount(async () => {
|
|
420
|
+
if ($clientConfig === (false as unknown as undefined)) {
|
|
421
|
+
$clientConfig = undefined;
|
|
422
|
+
$clientConfig = await User.getClientConfig();
|
|
423
|
+
}
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
onDestroy(() => {
|
|
427
|
+
User.off('login', onLogin);
|
|
428
|
+
User.off('logout', onLogout);
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
async function save() {
|
|
432
|
+
if ($user == null) {
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
failureMessage = undefined;
|
|
437
|
+
loading = true;
|
|
438
|
+
|
|
439
|
+
if ($clientConfig?.emailUsernames) {
|
|
440
|
+
$user.username = $user.email;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
try {
|
|
444
|
+
if (await $user.$save()) {
|
|
445
|
+
originalEmail = $user.email;
|
|
446
|
+
open = false;
|
|
447
|
+
usernameVerifiedMessage = undefined;
|
|
448
|
+
emailVerifiedMessage = undefined;
|
|
449
|
+
} else {
|
|
450
|
+
failureMessage = 'Error saving account changes.';
|
|
451
|
+
}
|
|
452
|
+
} catch (e: any) {
|
|
453
|
+
failureMessage = e?.message;
|
|
454
|
+
}
|
|
455
|
+
loading = false;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
function checkUsername() {
|
|
459
|
+
usernameVerified = undefined;
|
|
460
|
+
usernameVerifiedMessage = undefined;
|
|
461
|
+
if (usernameTimer) {
|
|
462
|
+
clearTimeout(usernameTimer);
|
|
463
|
+
usernameTimer = undefined;
|
|
464
|
+
}
|
|
465
|
+
usernameTimer = setTimeout(async () => {
|
|
466
|
+
try {
|
|
467
|
+
const data = await $user?.$checkUsername();
|
|
468
|
+
usernameVerified = data?.result ?? false;
|
|
469
|
+
usernameVerifiedMessage =
|
|
470
|
+
data?.message ?? 'Error getting verification.';
|
|
471
|
+
} catch (e: any) {
|
|
472
|
+
usernameVerified = false;
|
|
473
|
+
usernameVerifiedMessage = e?.message;
|
|
474
|
+
}
|
|
475
|
+
}, 400);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
function checkEmail() {
|
|
479
|
+
emailVerified = undefined;
|
|
480
|
+
emailVerifiedMessage = undefined;
|
|
481
|
+
if (emailTimer) {
|
|
482
|
+
clearTimeout(emailTimer);
|
|
483
|
+
emailTimer = undefined;
|
|
484
|
+
}
|
|
485
|
+
emailTimer = setTimeout(async () => {
|
|
486
|
+
try {
|
|
487
|
+
const data = await $user?.$checkEmail();
|
|
488
|
+
emailVerified = data?.result ?? false;
|
|
489
|
+
emailVerifiedMessage = data?.message ?? 'Error getting verification.';
|
|
490
|
+
} catch (e: any) {
|
|
491
|
+
emailVerified = false;
|
|
492
|
+
emailVerifiedMessage = e?.message;
|
|
493
|
+
}
|
|
494
|
+
}, 400);
|
|
495
|
+
}
|
|
496
|
+
</script>
|
|
497
|
+
|
|
498
|
+
<style>
|
|
499
|
+
:global(.mdc-dialog .mdc-dialog__surface.tilmeld-account-dialog-surface) {
|
|
500
|
+
width: 360px;
|
|
501
|
+
max-width: calc(100vw - 32px);
|
|
502
|
+
}
|
|
503
|
+
.tilmeld-account-failure {
|
|
504
|
+
margin-top: 1em;
|
|
505
|
+
color: var(--mdc-theme-error, #f00);
|
|
506
|
+
}
|
|
507
|
+
.tilmeld-account-action {
|
|
508
|
+
margin-top: 1em;
|
|
509
|
+
}
|
|
510
|
+
.tilmeld-account-loading {
|
|
511
|
+
display: flex;
|
|
512
|
+
justify-content: center;
|
|
513
|
+
align-items: center;
|
|
514
|
+
}
|
|
515
|
+
</style>
|