@hadoku/task-ui-components 4.22.1 → 4.22.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.
@@ -1,7 +1,9 @@
1
1
  import { jsxs, jsx, Fragment } from "react/jsx-runtime";
2
- import { useState, useRef, useCallback, useEffect } from "react";
2
+ import { useState, useRef, useId, useCallback, useEffect } from "react";
3
+ import { useSettingsTab } from "@wolffm/themes";
3
4
  import { SettingsIcon } from "./ThemeIcons.js";
4
- import { prefetchSettings, setDisplayName, loadSignIn, switchKeyHref, loadSignInClient, passkeyHref, setContentLevel, passkeysSupported, deleteOwnAccount } from "../lib/settingsClient.js";
5
+ import { AvatarPicker } from "./AvatarPicker.js";
6
+ import { passkeysSupported, prefetchSettings, setDisplayName, loadSignIn, switchKeyHref, loadSignInClient, passkeyHref, setContentLevel, deleteOwnAccount } from "../lib/settingsClient.js";
5
7
  /* empty css */
6
8
  function untilAborted(signal) {
7
9
  return new Promise((_, reject) => {
@@ -25,7 +27,6 @@ const PASSKEY_DOT = {
25
27
  pointerEvents: "none"
26
28
  };
27
29
  const PASSKEY_PULSE_CLASS = "settings-popout__btn--pulse";
28
- const PASSKEY_PULSE_STYLE_ID = "settings-passkey-pulse";
29
30
  const PASSKEY_PULSE_CSS = `
30
31
  @keyframes settings-passkey-pulse {
31
32
  0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--color-primary) 70%, transparent); }
@@ -38,14 +39,67 @@ const PASSKEY_PULSE_CSS = `
38
39
  }
39
40
  }
40
41
  `;
41
- function usePasskeyPulseStyle(active) {
42
+ const TABS_CSS = `
43
+ .settings-popout__top {
44
+ position: sticky;
45
+ top: -16px;
46
+ z-index: 1;
47
+ display: flex;
48
+ flex-direction: column;
49
+ gap: 10px;
50
+ margin: -16px -16px 0;
51
+ padding: 16px 16px 0;
52
+ background: var(--color-bg-card);
53
+ border-bottom: 1px solid var(--color-border);
54
+ }
55
+ .settings-popout__tabs {
56
+ display: flex;
57
+ gap: 2px;
58
+ margin: 0 -16px -1px;
59
+ padding: 0 16px;
60
+ overflow-x: auto;
61
+ scrollbar-width: thin;
62
+ }
63
+ .settings-popout__tab {
64
+ position: relative;
65
+ flex: none;
66
+ padding: 6px 10px;
67
+ border: 0;
68
+ border-bottom: 2px solid transparent;
69
+ background: transparent;
70
+ color: var(--color-text-secondary);
71
+ font: inherit;
72
+ font-size: 0.8rem;
73
+ font-weight: 600;
74
+ white-space: nowrap;
75
+ cursor: pointer;
76
+ }
77
+ .settings-popout__tab:hover {
78
+ color: var(--color-text);
79
+ }
80
+ .settings-popout__tab[aria-selected='true'] {
81
+ color: var(--color-text);
82
+ border-bottom-color: var(--color-primary);
83
+ }
84
+ .settings-popout__tab:focus-visible {
85
+ outline: 2px solid var(--color-primary);
86
+ outline-offset: -2px;
87
+ border-radius: 6px 6px 0 0;
88
+ }
89
+ .settings-popout__tabpanel {
90
+ display: flex;
91
+ flex-direction: column;
92
+ gap: 14px;
93
+ }
94
+ `;
95
+ function useInjectedStyle(id, css, active) {
42
96
  useEffect(() => {
43
- if (!active || document.getElementById(PASSKEY_PULSE_STYLE_ID)) return;
97
+ if (!active || document.getElementById(id)) return;
44
98
  const el = document.createElement("style");
45
- el.id = PASSKEY_PULSE_STYLE_ID;
46
- el.textContent = PASSKEY_PULSE_CSS;
99
+ el.id = id;
100
+ el.textContent = css;
47
101
  document.head.appendChild(el);
48
- }, [active]);
102
+ }, [id, css, active]);
49
103
  }
50
104
  const TIER_LABEL = {
51
105
  admin: "Administrator",
@@ -59,10 +113,15 @@ function ConnectedSettings({
59
113
  userType: userTypeProp,
60
114
  name: nameProp,
61
115
  onNameChange,
62
- children
116
+ children,
117
+ appTabs
63
118
  }) {
64
119
  const [open, setOpen] = useState(false);
65
120
  const rootRef = useRef(null);
121
+ const tabIdBase = useId();
122
+ const tabRefs = useRef(/* @__PURE__ */ new Map());
123
+ const [savedTab, saveTab] = useSettingsTab();
124
+ const [pickedTab, setPickedTab] = useState(null);
66
125
  const [userType, setUserType] = useState(userTypeProp ?? "public");
67
126
  const [resolvedName, setResolvedName] = useState(nameProp ?? null);
68
127
  const [nameDraft, setNameDraft] = useState(resolvedName ?? "");
@@ -82,12 +141,48 @@ function ConnectedSettings({
82
141
  const [deleteError, setDeleteError] = useState(null);
83
142
  const passkeyCtl = useRef(null);
84
143
  const cancelPasskey = useCallback((reason) => passkeyCtl.current?.abort(reason), []);
144
+ const [avatarOpen, setAvatarOpen] = useState(false);
85
145
  const [content, setContent] = useState(null);
86
146
  const [levelSaving, setLevelSaving] = useState(false);
87
147
  const [resolving, setResolving] = useState(true);
88
148
  const showContentPill = userType !== "public";
89
149
  const needsPasskey = !resolving && userType !== "public" && passkeyCount === 0 && !passkeySaved && passkeysSupported();
90
- usePasskeyPulseStyle(needsPasskey);
150
+ useInjectedStyle("settings-passkey-pulse", PASSKEY_PULSE_CSS, needsPasskey);
151
+ useInjectedStyle("settings-tabs", TABS_CSS, open);
152
+ const tabs = [
153
+ { id: "profile", label: "Profile" },
154
+ ...!resolving && showContentPill ? [{ id: "content", label: "Content" }] : [],
155
+ // The gear's dot asks for a passkey; this one says where Save passkey is.
156
+ { id: "account", label: "Account", dot: needsPasskey },
157
+ ...children ? [{ id: "app:", label: "App" }] : [],
158
+ ...(appTabs ?? []).map((t) => ({ id: `app:${t.id}`, label: t.label }))
159
+ ];
160
+ const tabIds = tabs.map((t) => t.id);
161
+ const defaultTab = tabIds.find((id) => id.startsWith("app:")) ?? "profile";
162
+ const activeTab = [pickedTab, savedTab].find((id) => id !== null && tabIds.includes(id)) ?? defaultTab;
163
+ const appTab = appTabs?.find((t) => `app:${t.id}` === activeTab);
164
+ const tabDomId = (id) => `${tabIdBase}tab-${id}`;
165
+ const panelDomId = `${tabIdBase}panel`;
166
+ const selectTab = useCallback(
167
+ (id) => {
168
+ setPickedTab(id);
169
+ saveTab(id);
170
+ },
171
+ [saveTab]
172
+ );
173
+ const onTabKey = (e) => {
174
+ const i = tabIds.indexOf(activeTab);
175
+ const n = tabIds.length;
176
+ const next = e.key === "ArrowRight" ? tabIds[(i + 1) % n] : e.key === "ArrowLeft" ? tabIds[(i - 1 + n) % n] : e.key === "Home" ? tabIds[0] : e.key === "End" ? tabIds[n - 1] : void 0;
177
+ if (next === void 0) return;
178
+ e.preventDefault();
179
+ selectTab(next);
180
+ tabRefs.current.get(next)?.focus();
181
+ };
182
+ useEffect(() => {
183
+ if (open)
184
+ tabRefs.current.get(activeTab)?.scrollIntoView({ block: "nearest", inline: "nearest" });
185
+ }, [open, activeTab]);
91
186
  useEffect(() => {
92
187
  if (userTypeProp !== void 0) setUserType(userTypeProp);
93
188
  }, [userTypeProp]);
@@ -260,283 +355,344 @@ function ConnectedSettings({
260
355
  ),
261
356
  open && /* @__PURE__ */ jsx("div", { className: "settings-popout__overlay", onClick: () => setOpen(false) }),
262
357
  open && /* @__PURE__ */ jsxs("div", { className: "settings-popout__panel", role: "dialog", "aria-label": "User settings", children: [
263
- /* @__PURE__ */ jsx("div", { className: "settings-popout__header", children: "Settings" }),
264
- /* @__PURE__ */ jsxs("section", { className: "settings-popout__row", children: [
265
- /* @__PURE__ */ jsx("span", { className: "settings-popout__label", children: "Access tier" }),
266
- resolving ? /* @__PURE__ */ jsx("span", { className: "settings-popout__value", children: "…" }) : /* @__PURE__ */ jsx("span", { className: `settings-popout__tier settings-popout__tier--${userType}`, children: TIER_LABEL[userType] })
267
- ] }),
268
- /* @__PURE__ */ jsxs("section", { className: "settings-popout__row", children: [
269
- /* @__PURE__ */ jsx("span", { className: "settings-popout__label", children: "Display name" }),
270
- resolving ? /* @__PURE__ */ jsx("span", { className: "settings-popout__value", children: "…" }) : editingName ? /* @__PURE__ */ jsxs("span", { className: "settings-popout__inline", children: [
271
- /* @__PURE__ */ jsx(
272
- "input",
273
- {
274
- className: "settings-popout__input",
275
- value: nameDraft,
276
- maxLength: 64,
277
- autoFocus: true,
278
- disabled: nameSaving,
279
- "aria-invalid": nameError !== null,
280
- "aria-describedby": nameError ? "settings-popout-name-error" : void 0,
281
- onChange: (e) => {
282
- setNameDraft(e.target.value);
283
- setNameError(null);
284
- },
285
- onKeyDown: (e) => {
286
- if (e.key === "Enter") {
287
- saveName().catch(reportErr);
288
- }
289
- if (e.key === "Escape") {
290
- e.stopPropagation();
291
- setEditingName(false);
292
- setNameError(null);
293
- setNameDraft(displayName ?? "");
294
- }
295
- }
296
- }
297
- ),
298
- /* @__PURE__ */ jsx(
299
- "button",
300
- {
301
- type: "button",
302
- className: "settings-popout__btn settings-popout__btn--primary",
303
- disabled: nameSaving,
304
- onClick: () => {
305
- saveName().catch(reportErr);
306
- },
307
- children: nameSaving ? "…" : "Save"
308
- }
309
- ),
310
- nameError && /* @__PURE__ */ jsx(
311
- "span",
312
- {
313
- id: "settings-popout-name-error",
314
- className: "settings-popout__error",
315
- role: "alert",
316
- children: nameError
317
- }
318
- )
319
- ] }) : /* @__PURE__ */ jsx(
320
- "button",
358
+ /* @__PURE__ */ jsxs("div", { className: "settings-popout__top", children: [
359
+ /* @__PURE__ */ jsx("div", { className: "settings-popout__header", children: "Settings" }),
360
+ /* @__PURE__ */ jsx(
361
+ "div",
321
362
  {
322
- type: "button",
323
- className: "settings-popout__value settings-popout__value--editable",
324
- "aria-label": "Edit display name",
325
- onClick: () => {
326
- setNameDraft(displayName ?? "");
327
- setEditingName(true);
328
- },
329
- children: displayName || "Unnamed"
330
- }
331
- )
332
- ] }),
333
- !resolving && userType !== "public" && /* @__PURE__ */ jsxs("section", { className: "settings-popout__row", children: [
334
- /* @__PURE__ */ jsx("span", { className: "settings-popout__label", children: "Avatar" }),
335
- /* @__PURE__ */ jsx("a", { className: "settings-popout__btn", href: "/account", target: "_blank", rel: "noopener", children: "Change" })
336
- ] }),
337
- !resolving && showContentPill && /* @__PURE__ */ jsxs("section", { className: "settings-popout__row settings-popout__row--stack", children: [
338
- /* @__PURE__ */ jsx("span", { className: "settings-popout__label", children: "Content visibility" }),
339
- content ? /* @__PURE__ */ jsxs(Fragment, { children: [
340
- /* @__PURE__ */ jsx(
341
- "div",
342
- {
343
- className: "settings-popout__pill",
344
- role: "radiogroup",
345
- "aria-label": "Content visibility level",
346
- children: Array.from({ length: content.maxLevel }, (_, i) => i + 1).map((lvl) => /* @__PURE__ */ jsx(
347
- "button",
348
- {
349
- type: "button",
350
- role: "radio",
351
- "aria-checked": lvl === content.level,
352
- "aria-label": `Level ${lvl}`,
353
- className: `settings-popout__seg${lvl <= content.level ? " settings-popout__seg--filled" : ""}`,
354
- disabled: levelSaving,
355
- onClick: () => {
356
- pickLevel(lvl).catch(reportErr);
357
- },
358
- children: lvl
363
+ className: "settings-popout__tabs",
364
+ role: "tablist",
365
+ "aria-label": "Settings sections",
366
+ onKeyDown: onTabKey,
367
+ children: tabs.map((t) => /* @__PURE__ */ jsxs(
368
+ "button",
369
+ {
370
+ ref: (el) => {
371
+ if (el) tabRefs.current.set(t.id, el);
372
+ else tabRefs.current.delete(t.id);
359
373
  },
360
- lvl
361
- ))
362
- }
363
- ),
364
- /* @__PURE__ */ jsx("span", { className: "settings-popout__hint", children: "Higher levels reveal more mature content across apps. Level 1 is safe-for-everyone." })
365
- ] }) : (
366
- // Not "Loading…" — the fetch has already settled by the time
367
- // this section renders at all, so a null here means it failed.
368
- /* @__PURE__ */ jsx("span", { className: "settings-popout__hint", children: "Currently unavailable." })
374
+ type: "button",
375
+ role: "tab",
376
+ id: tabDomId(t.id),
377
+ className: "settings-popout__tab",
378
+ "aria-selected": t.id === activeTab,
379
+ "aria-controls": panelDomId,
380
+ tabIndex: t.id === activeTab ? 0 : -1,
381
+ onClick: () => selectTab(t.id),
382
+ children: [
383
+ t.label,
384
+ t.dot && /* @__PURE__ */ jsx("span", { style: PASSKEY_DOT, "aria-hidden": "true" })
385
+ ]
386
+ },
387
+ t.id
388
+ ))
389
+ }
369
390
  )
370
391
  ] }),
371
- keyFormOpen ? /* @__PURE__ */ jsx("section", { className: "settings-popout__row settings-popout__row--stack", children: /* @__PURE__ */ jsxs(
372
- "form",
392
+ /* @__PURE__ */ jsxs(
393
+ "div",
373
394
  {
374
- className: "settings-popout__key-form",
375
- onSubmit: (e) => {
376
- e.preventDefault();
377
- submitKey().catch(reportErr);
378
- },
395
+ className: "settings-popout__tabpanel",
396
+ role: "tabpanel",
397
+ id: panelDomId,
398
+ "aria-labelledby": tabDomId(activeTab),
379
399
  children: [
380
- /* @__PURE__ */ jsx(
381
- "input",
382
- {
383
- type: "text",
384
- name: "username",
385
- autoComplete: "username",
386
- value: "hadoku",
387
- readOnly: true,
388
- hidden: true
389
- }
390
- ),
391
- /* @__PURE__ */ jsx("label", { className: "settings-popout__label", htmlFor: "settings-popout-key", children: "Access key" }),
392
- /* @__PURE__ */ jsxs("span", { className: "settings-popout__inline", children: [
393
- /* @__PURE__ */ jsx(
394
- "input",
395
- {
396
- id: "settings-popout-key",
397
- className: "settings-popout__input",
398
- type: "password",
399
- name: "password",
400
- autoComplete: "current-password",
401
- autoCapitalize: "none",
402
- spellCheck: false,
403
- autoFocus: true,
404
- value: keyDraft,
405
- disabled: keyBusy,
406
- "aria-invalid": keyError !== null,
407
- "aria-describedby": keyError ? "settings-popout-key-error" : void 0,
408
- onChange: (e) => setKeyDraft(e.target.value)
409
- }
410
- ),
411
- /* @__PURE__ */ jsx(
412
- "button",
400
+ activeTab === "profile" && /* @__PURE__ */ jsxs(Fragment, { children: [
401
+ /* @__PURE__ */ jsxs("section", { className: "settings-popout__row", children: [
402
+ /* @__PURE__ */ jsx("span", { className: "settings-popout__label", children: "Access tier" }),
403
+ resolving ? /* @__PURE__ */ jsx("span", { className: "settings-popout__value", children: "…" }) : /* @__PURE__ */ jsx("span", { className: `settings-popout__tier settings-popout__tier--${userType}`, children: TIER_LABEL[userType] })
404
+ ] }),
405
+ /* @__PURE__ */ jsxs("section", { className: "settings-popout__row", children: [
406
+ /* @__PURE__ */ jsx("span", { className: "settings-popout__label", children: "Display name" }),
407
+ resolving ? /* @__PURE__ */ jsx("span", { className: "settings-popout__value", children: "…" }) : editingName ? /* @__PURE__ */ jsxs("span", { className: "settings-popout__inline", children: [
408
+ /* @__PURE__ */ jsx(
409
+ "input",
410
+ {
411
+ className: "settings-popout__input",
412
+ value: nameDraft,
413
+ maxLength: 64,
414
+ autoFocus: true,
415
+ disabled: nameSaving,
416
+ "aria-invalid": nameError !== null,
417
+ "aria-describedby": nameError ? "settings-popout-name-error" : void 0,
418
+ onChange: (e) => {
419
+ setNameDraft(e.target.value);
420
+ setNameError(null);
421
+ },
422
+ onKeyDown: (e) => {
423
+ if (e.key === "Enter") {
424
+ saveName().catch(reportErr);
425
+ }
426
+ if (e.key === "Escape") {
427
+ e.stopPropagation();
428
+ setEditingName(false);
429
+ setNameError(null);
430
+ setNameDraft(displayName ?? "");
431
+ }
432
+ }
433
+ }
434
+ ),
435
+ /* @__PURE__ */ jsx(
436
+ "button",
437
+ {
438
+ type: "button",
439
+ className: "settings-popout__btn settings-popout__btn--primary",
440
+ disabled: nameSaving,
441
+ onClick: () => {
442
+ saveName().catch(reportErr);
443
+ },
444
+ children: nameSaving ? "…" : "Save"
445
+ }
446
+ ),
447
+ nameError && /* @__PURE__ */ jsx(
448
+ "span",
449
+ {
450
+ id: "settings-popout-name-error",
451
+ className: "settings-popout__error",
452
+ role: "alert",
453
+ children: nameError
454
+ }
455
+ )
456
+ ] }) : /* @__PURE__ */ jsx(
457
+ "button",
458
+ {
459
+ type: "button",
460
+ className: "settings-popout__value settings-popout__value--editable",
461
+ "aria-label": "Edit display name",
462
+ onClick: () => {
463
+ setNameDraft(displayName ?? "");
464
+ setEditingName(true);
465
+ },
466
+ children: displayName || "Unnamed"
467
+ }
468
+ )
469
+ ] }),
470
+ !resolving && userType !== "public" && /* @__PURE__ */ jsxs("section", { className: "settings-popout__row settings-popout__row--stack", children: [
471
+ /* @__PURE__ */ jsxs("span", { className: "settings-popout__inline", children: [
472
+ /* @__PURE__ */ jsx("span", { className: "settings-popout__label settings-popout__label--grow", children: "Avatar" }),
473
+ /* @__PURE__ */ jsx(
474
+ "button",
475
+ {
476
+ type: "button",
477
+ className: "settings-popout__btn",
478
+ "aria-expanded": avatarOpen,
479
+ onClick: () => setAvatarOpen((v) => !v),
480
+ children: avatarOpen ? "Done" : "Change"
481
+ }
482
+ )
483
+ ] }),
484
+ avatarOpen && /* @__PURE__ */ jsx(AvatarPicker, {})
485
+ ] })
486
+ ] }),
487
+ activeTab === "content" && /* @__PURE__ */ jsxs("section", { className: "settings-popout__row settings-popout__row--stack", children: [
488
+ /* @__PURE__ */ jsx("span", { className: "settings-popout__label", children: "Content visibility" }),
489
+ content ? /* @__PURE__ */ jsxs(Fragment, { children: [
490
+ /* @__PURE__ */ jsx(
491
+ "div",
492
+ {
493
+ className: "settings-popout__pill",
494
+ role: "radiogroup",
495
+ "aria-label": "Content visibility level",
496
+ children: Array.from({ length: content.maxLevel }, (_, i) => i + 1).map((lvl) => /* @__PURE__ */ jsx(
497
+ "button",
498
+ {
499
+ type: "button",
500
+ role: "radio",
501
+ "aria-checked": lvl === content.level,
502
+ "aria-label": `Level ${lvl}`,
503
+ className: `settings-popout__seg${lvl <= content.level ? " settings-popout__seg--filled" : ""}`,
504
+ disabled: levelSaving,
505
+ onClick: () => {
506
+ pickLevel(lvl).catch(reportErr);
507
+ },
508
+ children: lvl
509
+ },
510
+ lvl
511
+ ))
512
+ }
513
+ ),
514
+ /* @__PURE__ */ jsx("span", { className: "settings-popout__hint", children: "Higher levels reveal more mature content across apps. Level 1 is safe-for-everyone." })
515
+ ] }) : (
516
+ // Not "Loading…" — the fetch has already settled by the time
517
+ // this section renders at all, so a null here means it failed.
518
+ /* @__PURE__ */ jsx("span", { className: "settings-popout__hint", children: "Currently unavailable." })
519
+ )
520
+ ] }),
521
+ activeTab === "account" && /* @__PURE__ */ jsxs(Fragment, { children: [
522
+ keyFormOpen ? /* @__PURE__ */ jsx("section", { className: "settings-popout__row settings-popout__row--stack", children: /* @__PURE__ */ jsxs(
523
+ "form",
413
524
  {
414
- type: "submit",
415
- className: "settings-popout__btn settings-popout__btn--primary",
416
- disabled: keyBusy,
417
- children: keyBusy ? "Signing in…" : "Sign in"
525
+ className: "settings-popout__key-form",
526
+ onSubmit: (e) => {
527
+ e.preventDefault();
528
+ submitKey().catch(reportErr);
529
+ },
530
+ children: [
531
+ /* @__PURE__ */ jsx(
532
+ "input",
533
+ {
534
+ type: "text",
535
+ name: "username",
536
+ autoComplete: "username",
537
+ value: "hadoku",
538
+ readOnly: true,
539
+ hidden: true
540
+ }
541
+ ),
542
+ /* @__PURE__ */ jsx("label", { className: "settings-popout__label", htmlFor: "settings-popout-key", children: "Access key" }),
543
+ /* @__PURE__ */ jsxs("span", { className: "settings-popout__inline", children: [
544
+ /* @__PURE__ */ jsx(
545
+ "input",
546
+ {
547
+ id: "settings-popout-key",
548
+ className: "settings-popout__input",
549
+ type: "password",
550
+ name: "password",
551
+ autoComplete: "current-password",
552
+ autoCapitalize: "none",
553
+ spellCheck: false,
554
+ autoFocus: true,
555
+ value: keyDraft,
556
+ disabled: keyBusy,
557
+ "aria-invalid": keyError !== null,
558
+ "aria-describedby": keyError ? "settings-popout-key-error" : void 0,
559
+ onChange: (e) => setKeyDraft(e.target.value)
560
+ }
561
+ ),
562
+ /* @__PURE__ */ jsx(
563
+ "button",
564
+ {
565
+ type: "submit",
566
+ className: "settings-popout__btn settings-popout__btn--primary",
567
+ disabled: keyBusy,
568
+ children: keyBusy ? "Signing in…" : "Sign in"
569
+ }
570
+ )
571
+ ] }),
572
+ passkeysSupported() && /* @__PURE__ */ jsx("span", { className: "settings-popout__inline", children: /* @__PURE__ */ jsx(
573
+ "button",
574
+ {
575
+ type: "button",
576
+ className: "settings-popout__btn",
577
+ disabled: keyBusy,
578
+ onClick: () => {
579
+ signInWithPasskey().catch(reportErr);
580
+ },
581
+ children: passkeyMode === "signin" ? "Waiting for passkey…" : "Use a passkey"
582
+ }
583
+ ) }),
584
+ passkeyMode === "signin" && /* @__PURE__ */ jsx("span", { className: "settings-popout__inline", children: /* @__PURE__ */ jsx(
585
+ "button",
586
+ {
587
+ type: "button",
588
+ className: "settings-popout__btn",
589
+ onClick: () => cancelPasskey(),
590
+ children: "Cancel"
591
+ }
592
+ ) }),
593
+ keyError && /* @__PURE__ */ jsx(
594
+ "span",
595
+ {
596
+ id: "settings-popout-key-error",
597
+ className: "settings-popout__error",
598
+ role: "alert",
599
+ children: keyError
600
+ }
601
+ )
602
+ ]
418
603
  }
419
- )
604
+ ) }) : /* @__PURE__ */ jsxs("section", { className: "settings-popout__row settings-popout__row--stack", children: [
605
+ /* @__PURE__ */ jsxs("span", { className: "settings-popout__inline", children: [
606
+ /* @__PURE__ */ jsx("span", { className: "settings-popout__label settings-popout__label--grow", children: "Access key" }),
607
+ !resolving && userType !== "public" && passkeysSupported() && /* @__PURE__ */ jsx(
608
+ "button",
609
+ {
610
+ type: "button",
611
+ className: `settings-popout__btn${needsPasskey ? ` settings-popout__btn--primary ${PASSKEY_PULSE_CLASS}` : ""}`,
612
+ disabled: passkeyMode !== null || passkeySaved,
613
+ onClick: () => {
614
+ savePasskey().catch(reportErr);
615
+ },
616
+ children: passkeySaved ? "Passkey saved" : passkeyMode === "save" ? "Saving…" : "Save passkey"
617
+ }
618
+ ),
619
+ /* @__PURE__ */ jsx(
620
+ "button",
621
+ {
622
+ type: "button",
623
+ className: "settings-popout__btn",
624
+ disabled: passkeyMode === "save",
625
+ onClick: openKeyForm,
626
+ children: "Switch key"
627
+ }
628
+ )
629
+ ] }),
630
+ passkeyMode === "save" && /* @__PURE__ */ jsx("span", { className: "settings-popout__inline", children: /* @__PURE__ */ jsx(
631
+ "button",
632
+ {
633
+ type: "button",
634
+ className: "settings-popout__btn",
635
+ onClick: () => cancelPasskey(),
636
+ children: "Cancel"
637
+ }
638
+ ) }),
639
+ passkeyError && /* @__PURE__ */ jsx("span", { className: "settings-popout__error", role: "alert", children: passkeyError })
640
+ ] }),
641
+ !resolving && userType === "member" && /* @__PURE__ */ jsxs("section", { className: "settings-popout__row settings-popout__row--stack", children: [
642
+ /* @__PURE__ */ jsxs("span", { className: "settings-popout__inline", children: [
643
+ /* @__PURE__ */ jsx("span", { className: "settings-popout__label settings-popout__label--grow", children: "Account" }),
644
+ deleteStage === "idle" ? /* @__PURE__ */ jsx(
645
+ "button",
646
+ {
647
+ type: "button",
648
+ className: "settings-popout__btn",
649
+ onClick: () => {
650
+ setDeleteError(null);
651
+ setDeleteStage("confirm");
652
+ },
653
+ children: "Delete account"
654
+ }
655
+ ) : /* @__PURE__ */ jsx(
656
+ "button",
657
+ {
658
+ type: "button",
659
+ className: "settings-popout__btn",
660
+ disabled: deleteStage === "busy",
661
+ onClick: () => setDeleteStage("idle"),
662
+ children: "Keep"
663
+ }
664
+ )
665
+ ] }),
666
+ deleteStage !== "idle" && /* @__PURE__ */ jsx("span", { className: "settings-popout__inline", children: /* @__PURE__ */ jsx(
667
+ "button",
668
+ {
669
+ type: "button",
670
+ className: "settings-popout__btn settings-popout__btn--primary",
671
+ disabled: deleteStage === "busy",
672
+ onClick: () => {
673
+ setDeleteStage("busy");
674
+ deleteOwnAccount().then((deleted) => {
675
+ if (!deleted) {
676
+ setDeleteError("Account not deleted");
677
+ setDeleteStage("confirm");
678
+ return;
679
+ }
680
+ localStorage.removeItem("hadoku_session_id");
681
+ localStorage.removeItem("hadoku_user_type");
682
+ window.location.assign("/");
683
+ }).catch(reportErr);
684
+ },
685
+ children: deleteStage === "busy" ? "Deleting…" : "Delete for good"
686
+ }
687
+ ) }),
688
+ deleteError && /* @__PURE__ */ jsx("span", { className: "settings-popout__error", role: "alert", children: deleteError })
689
+ ] })
420
690
  ] }),
421
- passkeysSupported() && /* @__PURE__ */ jsx("span", { className: "settings-popout__inline", children: /* @__PURE__ */ jsx(
422
- "button",
423
- {
424
- type: "button",
425
- className: "settings-popout__btn",
426
- disabled: keyBusy,
427
- onClick: () => {
428
- signInWithPasskey().catch(reportErr);
429
- },
430
- children: passkeyMode === "signin" ? "Waiting for passkey…" : "Use a passkey"
431
- }
432
- ) }),
433
- passkeyMode === "signin" && /* @__PURE__ */ jsx("span", { className: "settings-popout__inline", children: /* @__PURE__ */ jsx(
434
- "button",
435
- {
436
- type: "button",
437
- className: "settings-popout__btn",
438
- onClick: () => cancelPasskey(),
439
- children: "Cancel"
440
- }
441
- ) }),
442
- keyError && /* @__PURE__ */ jsx(
443
- "span",
444
- {
445
- id: "settings-popout-key-error",
446
- className: "settings-popout__error",
447
- role: "alert",
448
- children: keyError
449
- }
450
- )
691
+ activeTab === "app:" && /* @__PURE__ */ jsx("section", { className: "settings-popout__row settings-popout__row--stack", children }),
692
+ appTab && /* @__PURE__ */ jsx("section", { className: "settings-popout__row settings-popout__row--stack", children: appTab.render() })
451
693
  ]
452
694
  }
453
- ) }) : /* @__PURE__ */ jsxs("section", { className: "settings-popout__row settings-popout__row--stack", children: [
454
- /* @__PURE__ */ jsxs("span", { className: "settings-popout__inline", children: [
455
- /* @__PURE__ */ jsx("span", { className: "settings-popout__label settings-popout__label--grow", children: "Access key" }),
456
- !resolving && userType !== "public" && passkeysSupported() && /* @__PURE__ */ jsx(
457
- "button",
458
- {
459
- type: "button",
460
- className: `settings-popout__btn${needsPasskey ? ` settings-popout__btn--primary ${PASSKEY_PULSE_CLASS}` : ""}`,
461
- disabled: passkeyMode !== null || passkeySaved,
462
- onClick: () => {
463
- savePasskey().catch(reportErr);
464
- },
465
- children: passkeySaved ? "Passkey saved" : passkeyMode === "save" ? "Saving…" : "Save passkey"
466
- }
467
- ),
468
- /* @__PURE__ */ jsx(
469
- "button",
470
- {
471
- type: "button",
472
- className: "settings-popout__btn",
473
- disabled: passkeyMode === "save",
474
- onClick: openKeyForm,
475
- children: "Switch key"
476
- }
477
- )
478
- ] }),
479
- passkeyMode === "save" && /* @__PURE__ */ jsx("span", { className: "settings-popout__inline", children: /* @__PURE__ */ jsx(
480
- "button",
481
- {
482
- type: "button",
483
- className: "settings-popout__btn",
484
- onClick: () => cancelPasskey(),
485
- children: "Cancel"
486
- }
487
- ) }),
488
- passkeyError && /* @__PURE__ */ jsx("span", { className: "settings-popout__error", role: "alert", children: passkeyError })
489
- ] }),
490
- !resolving && userType === "member" && /* @__PURE__ */ jsxs("section", { className: "settings-popout__row settings-popout__row--stack", children: [
491
- /* @__PURE__ */ jsxs("span", { className: "settings-popout__inline", children: [
492
- /* @__PURE__ */ jsx("span", { className: "settings-popout__label settings-popout__label--grow", children: "Account" }),
493
- deleteStage === "idle" ? /* @__PURE__ */ jsx(
494
- "button",
495
- {
496
- type: "button",
497
- className: "settings-popout__btn",
498
- onClick: () => {
499
- setDeleteError(null);
500
- setDeleteStage("confirm");
501
- },
502
- children: "Delete account"
503
- }
504
- ) : /* @__PURE__ */ jsx(
505
- "button",
506
- {
507
- type: "button",
508
- className: "settings-popout__btn",
509
- disabled: deleteStage === "busy",
510
- onClick: () => setDeleteStage("idle"),
511
- children: "Keep"
512
- }
513
- )
514
- ] }),
515
- deleteStage !== "idle" && /* @__PURE__ */ jsx("span", { className: "settings-popout__inline", children: /* @__PURE__ */ jsx(
516
- "button",
517
- {
518
- type: "button",
519
- className: "settings-popout__btn settings-popout__btn--primary",
520
- disabled: deleteStage === "busy",
521
- onClick: () => {
522
- setDeleteStage("busy");
523
- deleteOwnAccount().then((deleted) => {
524
- if (!deleted) {
525
- setDeleteError("Account not deleted");
526
- setDeleteStage("confirm");
527
- return;
528
- }
529
- localStorage.removeItem("hadoku_session_id");
530
- localStorage.removeItem("hadoku_user_type");
531
- window.location.assign("/");
532
- }).catch(reportErr);
533
- },
534
- children: deleteStage === "busy" ? "Deleting…" : "Delete for good"
535
- }
536
- ) }),
537
- deleteError && /* @__PURE__ */ jsx("span", { className: "settings-popout__error", role: "alert", children: deleteError })
538
- ] }),
539
- children && /* @__PURE__ */ jsx("section", { className: "settings-popout__row settings-popout__row--stack settings-popout__app", children })
695
+ )
540
696
  ] })
541
697
  ] });
542
698
  }