@protobase/ui 0.46.8 → 0.46.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/account/change-email-dialog.js +1 -1
- package/dist/account/two-factor-setup.js +1 -1
- package/dist/app/confirm-sign-in-messages.js +22 -0
- package/dist/app/confirm-sign-in.js +88 -0
- package/dist/app/sensitive-field.js +10 -2
- package/dist/auth/auth-provider.js +3 -1
- package/dist/auth/code-form.js +1 -1
- package/dist/auth/two-factor-form.js +1 -1
- package/dist/index.js +1 -1
- package/dist/package.js +1 -1
- package/dist/shell/assets/app-axeIZ-10.js +9 -0
- package/dist/shell/assets/{index-oppnv6lh.js → index-1Ue_dZiA.js} +2 -2
- package/dist/shell/assets/{records-map-Ck8_EDiN.js → records-map-DPA8Y3C8.js} +2 -2
- package/dist/shell/assets/shared-protobase-ui-dWIf89ff.js +2 -0
- package/dist/shell/index.html +3 -3
- package/dist/shell/protobase.shell.json +1 -1
- package/dist/shell/protobase.sw.js +2 -2
- package/dist/shell-source-maps/assets/app-axeIZ-10.js.map +1 -0
- package/dist/shell-source-maps/assets/{index-oppnv6lh.js.map → index-1Ue_dZiA.js.map} +1 -1
- package/dist/shell-source-maps/assets/{records-map-Ck8_EDiN.js.map → records-map-DPA8Y3C8.js.map} +1 -1
- package/dist/shell-source-maps/assets/{shared-protobase-ui-DWI6ok-_.js.map → shared-protobase-ui-dWIf89ff.js.map} +1 -1
- package/package.json +4 -4
- package/dist/shell/assets/app-BxGBuUKh.js +0 -9
- package/dist/shell/assets/shared-protobase-ui-DWI6ok-_.js +0 -2
- package/dist/shell-source-maps/assets/app-BxGBuUKh.js.map +0 -1
|
@@ -2,9 +2,9 @@ import { text } from "./messages.js";
|
|
|
2
2
|
import { Button } from "../primitives/button.js";
|
|
3
3
|
import { Dialog } from "../primitives/dialog.js";
|
|
4
4
|
import { Input } from "../primitives/input.js";
|
|
5
|
+
import { OtpInput } from "../primitives/otp-input.js";
|
|
5
6
|
import { SectionError } from "./account-section.js";
|
|
6
7
|
import { authMessage } from "../auth/auth-messages.js";
|
|
7
|
-
import { OtpInput } from "../primitives/otp-input.js";
|
|
8
8
|
import { useState } from "react";
|
|
9
9
|
import { AuthError } from "@protobase/client";
|
|
10
10
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -2,8 +2,8 @@ import { text } from "./messages.js";
|
|
|
2
2
|
import { cn } from "../lib/cn.js";
|
|
3
3
|
import { Button } from "../primitives/button.js";
|
|
4
4
|
import { Input } from "../primitives/input.js";
|
|
5
|
-
import { authMessage } from "../auth/auth-messages.js";
|
|
6
5
|
import { OtpInput } from "../primitives/otp-input.js";
|
|
6
|
+
import { authMessage } from "../auth/auth-messages.js";
|
|
7
7
|
import { BackupCodes } from "./backup-codes.js";
|
|
8
8
|
import { TotpQr, totpKey } from "./totp-qr.js";
|
|
9
9
|
import { useState } from "react";
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { defineMessages } from "../locale/messages.js";
|
|
2
|
+
//#region src/app/confirm-sign-in-messages.ts
|
|
3
|
+
const text = defineMessages({
|
|
4
|
+
en: {
|
|
5
|
+
confirmTitle: "Confirm it's you",
|
|
6
|
+
confirmWhy: (minutes) => `Showing this needs a passkey or your authenticator app in the last ${minutes} minutes.`,
|
|
7
|
+
usePasskey: "Use a passkey",
|
|
8
|
+
authenticatorCode: "Code from your authenticator app",
|
|
9
|
+
confirm: "Confirm",
|
|
10
|
+
nothingToConfirmWith: "Add a passkey or an authenticator app on your account page first."
|
|
11
|
+
},
|
|
12
|
+
nl: {
|
|
13
|
+
confirmTitle: "Bevestig dat jij het bent",
|
|
14
|
+
confirmWhy: (minutes) => `Dit tonen vraagt in de laatste ${minutes} minuten een passkey of je authenticator-app.`,
|
|
15
|
+
usePasskey: "Passkey gebruiken",
|
|
16
|
+
authenticatorCode: "Code uit je authenticator-app",
|
|
17
|
+
confirm: "Bevestigen",
|
|
18
|
+
nothingToConfirmWith: "Voeg eerst een passkey of een authenticator-app toe op je accountpagina."
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
//#endregion
|
|
22
|
+
export { text };
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { useOptionalAuth } from "../auth/auth-provider.js";
|
|
2
|
+
import { Button } from "../primitives/button.js";
|
|
3
|
+
import { OtpInput } from "../primitives/otp-input.js";
|
|
4
|
+
import { text } from "./confirm-sign-in-messages.js";
|
|
5
|
+
import { useState } from "react";
|
|
6
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
+
import { useMutation, useQuery } from "@tanstack/react-query";
|
|
8
|
+
//#region src/app/confirm-sign-in.tsx
|
|
9
|
+
/**
|
|
10
|
+
* Asks the person to confirm it's them, with a passkey or their authenticator app, after the server refused something
|
|
11
|
+
* that needs a recent strong sign-in (`strong-sign-in-required`). It offers only what the account has; a passkey signs
|
|
12
|
+
* the browser in anew, a code confirms the session it has.
|
|
13
|
+
*/
|
|
14
|
+
const ConfirmSignIn = ({ minutes, onConfirmed }) => {
|
|
15
|
+
const auth = useOptionalAuth();
|
|
16
|
+
const [code, setCode] = useState("");
|
|
17
|
+
const methods = useQuery({
|
|
18
|
+
queryKey: ["account-sign-in-methods"],
|
|
19
|
+
queryFn: () => auth.session.account.signInMethods(),
|
|
20
|
+
enabled: Boolean(auth)
|
|
21
|
+
});
|
|
22
|
+
const passkey = useMutation({
|
|
23
|
+
mutationFn: () => auth.signInWithPasskey(),
|
|
24
|
+
onSuccess: onConfirmed
|
|
25
|
+
});
|
|
26
|
+
const authenticator = useMutation({
|
|
27
|
+
mutationFn: (entered) => auth.verifyTwoFactor({
|
|
28
|
+
method: "totp",
|
|
29
|
+
code: entered
|
|
30
|
+
}),
|
|
31
|
+
onSuccess: onConfirmed
|
|
32
|
+
});
|
|
33
|
+
const account = methods.data?.account;
|
|
34
|
+
const error = passkey.error ?? authenticator.error ?? methods.error;
|
|
35
|
+
return /* @__PURE__ */ jsxs("section", {
|
|
36
|
+
"aria-label": text("confirmTitle"),
|
|
37
|
+
className: "mt-2 flex flex-col gap-2 rounded-md border border-border bg-surface p-3",
|
|
38
|
+
children: [
|
|
39
|
+
/* @__PURE__ */ jsx("p", {
|
|
40
|
+
className: "font-medium",
|
|
41
|
+
children: text("confirmTitle")
|
|
42
|
+
}),
|
|
43
|
+
/* @__PURE__ */ jsx("p", {
|
|
44
|
+
className: "text-muted-foreground",
|
|
45
|
+
children: text("confirmWhy", minutes)
|
|
46
|
+
}),
|
|
47
|
+
account && account.passkeys > 0 && /* @__PURE__ */ jsx(Button, {
|
|
48
|
+
variant: "primary",
|
|
49
|
+
size: "sm",
|
|
50
|
+
className: "self-start",
|
|
51
|
+
loading: passkey.isPending,
|
|
52
|
+
onClick: () => passkey.mutate(),
|
|
53
|
+
children: text("usePasskey")
|
|
54
|
+
}),
|
|
55
|
+
account?.authenticatorApp && /* @__PURE__ */ jsxs("form", {
|
|
56
|
+
className: "flex flex-wrap items-end gap-2",
|
|
57
|
+
onSubmit: (event) => {
|
|
58
|
+
event.preventDefault();
|
|
59
|
+
authenticator.mutate(code.trim());
|
|
60
|
+
},
|
|
61
|
+
children: [/* @__PURE__ */ jsxs("label", {
|
|
62
|
+
className: "flex flex-col gap-1.5",
|
|
63
|
+
children: [text("authenticatorCode"), /* @__PURE__ */ jsx(OtpInput, {
|
|
64
|
+
name: "code",
|
|
65
|
+
required: true,
|
|
66
|
+
value: code,
|
|
67
|
+
invalid: authenticator.isError,
|
|
68
|
+
onChange: setCode,
|
|
69
|
+
onComplete: (entered) => authenticator.mutate(entered)
|
|
70
|
+
})]
|
|
71
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
72
|
+
type: "submit",
|
|
73
|
+
size: "sm",
|
|
74
|
+
loading: authenticator.isPending,
|
|
75
|
+
children: text("confirm")
|
|
76
|
+
})]
|
|
77
|
+
}),
|
|
78
|
+
account && account.passkeys === 0 && !account.authenticatorApp && /* @__PURE__ */ jsx("p", { children: text("nothingToConfirmWith") }),
|
|
79
|
+
error && /* @__PURE__ */ jsx("p", {
|
|
80
|
+
role: "alert",
|
|
81
|
+
className: "font-medium text-danger-text",
|
|
82
|
+
children: error.message
|
|
83
|
+
})
|
|
84
|
+
]
|
|
85
|
+
});
|
|
86
|
+
};
|
|
87
|
+
//#endregion
|
|
88
|
+
export { ConfirmSignIn };
|
|
@@ -6,15 +6,19 @@ import { displayValue } from "../live/display-value.js";
|
|
|
6
6
|
import { text } from "./messages.js";
|
|
7
7
|
import { toDraft } from "../live/field-values.js";
|
|
8
8
|
import { ErrorBanner } from "./error-banner.js";
|
|
9
|
+
import { ConfirmSignIn } from "./confirm-sign-in.js";
|
|
9
10
|
import { useState } from "react";
|
|
10
11
|
import { Eye, EyeOff } from "lucide-react";
|
|
12
|
+
import { isApiError } from "@protobase/client";
|
|
11
13
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
12
14
|
import { useMutation } from "@tanstack/react-query";
|
|
13
15
|
//#region src/app/sensitive-field.tsx
|
|
14
16
|
const mask = "••••••••••";
|
|
17
|
+
const strongSignInMinutes = (error) => isApiError(error) && error.slug === "strong-sign-in-required" ? error.problem.minutes ?? 15 : void 0;
|
|
15
18
|
/**
|
|
16
19
|
* A value hidden like a password. Showing a stored one asks the server for it, once per page visit; a write-only one has
|
|
17
|
-
* none to show, so its eye only unmasks what was typed.
|
|
20
|
+
* none to show, so its eye only unmasks what was typed. When the server wants a recent strong sign-in first, the field
|
|
21
|
+
* asks the person to confirm it's them, then shows the value.
|
|
18
22
|
*/
|
|
19
23
|
const SensitiveField = ({ field, hints, value, reveal, locked, invalid, placeholder, onChange }) => {
|
|
20
24
|
const [shown, setShown] = useState(false);
|
|
@@ -38,7 +42,11 @@ const SensitiveField = ({ field, hints, value, reveal, locked, invalid, placehol
|
|
|
38
42
|
onClick: toggle,
|
|
39
43
|
children: !stored.isPending && (shown ? /* @__PURE__ */ jsx(EyeOff, { className: "size-3.5" }) : /* @__PURE__ */ jsx(Eye, { className: "size-3.5" }))
|
|
40
44
|
});
|
|
41
|
-
const
|
|
45
|
+
const minutes = strongSignInMinutes(stored.error);
|
|
46
|
+
const error = minutes !== void 0 ? /* @__PURE__ */ jsx(ConfirmSignIn, {
|
|
47
|
+
minutes,
|
|
48
|
+
onConfirmed: () => stored.mutate()
|
|
49
|
+
}) : stored.error && /* @__PURE__ */ jsx("div", {
|
|
42
50
|
className: "mt-2",
|
|
43
51
|
children: /* @__PURE__ */ jsx(ErrorBanner, {
|
|
44
52
|
title: text("couldNotShow", label),
|
|
@@ -12,6 +12,8 @@ const useAuth = () => {
|
|
|
12
12
|
if (!api) throw new Error("useAuth must be used inside AuthProvider");
|
|
13
13
|
return api;
|
|
14
14
|
};
|
|
15
|
+
/** The auth state and steps inside `AuthProvider`, `null` outside it, for a part that also works without one. */
|
|
16
|
+
const useOptionalAuth = () => useContext(AuthContext);
|
|
15
17
|
/**
|
|
16
18
|
* Finds out whether the server has a login and who is signed in, and keeps that state for the app. Without a `session`,
|
|
17
19
|
* it makes one on `origin`, the page's own by default; in the iOS or Android app, with its `nativeBridge`, the session
|
|
@@ -216,4 +218,4 @@ const AuthProvider = ({ session: given, origin, nativeBridge, children }) => {
|
|
|
216
218
|
});
|
|
217
219
|
};
|
|
218
220
|
//#endregion
|
|
219
|
-
export { AuthProvider, useAuth };
|
|
221
|
+
export { AuthProvider, useAuth, useOptionalAuth };
|
package/dist/auth/code-form.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { text } from "./messages.js";
|
|
2
2
|
import { Button } from "../primitives/button.js";
|
|
3
|
-
import { AuthLayout } from "./auth-layout.js";
|
|
4
3
|
import { OtpInput } from "../primitives/otp-input.js";
|
|
4
|
+
import { AuthLayout } from "./auth-layout.js";
|
|
5
5
|
import { ResendCodeButton } from "./resend-code-button.js";
|
|
6
6
|
import { useEffect, useRef, useState } from "react";
|
|
7
7
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { text } from "./messages.js";
|
|
2
2
|
import { Button } from "../primitives/button.js";
|
|
3
3
|
import { Input } from "../primitives/input.js";
|
|
4
|
+
import { OtpInput } from "../primitives/otp-input.js";
|
|
4
5
|
import { Checkbox } from "../primitives/checkbox.js";
|
|
5
6
|
import { AuthLayout } from "./auth-layout.js";
|
|
6
|
-
import { OtpInput } from "../primitives/otp-input.js";
|
|
7
7
|
import { useState } from "react";
|
|
8
8
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
9
9
|
//#region src/auth/two-factor-form.tsx
|
package/dist/index.js
CHANGED
|
@@ -12,6 +12,7 @@ import { AppLink, useAppAddress } from "./app/use-app-address.js";
|
|
|
12
12
|
import { Segmented } from "./primitives/segmented.js";
|
|
13
13
|
import { Textarea } from "./primitives/textarea.js";
|
|
14
14
|
import { useList } from "./data/use-list.js";
|
|
15
|
+
import { OtpInput } from "./primitives/otp-input.js";
|
|
15
16
|
import { definePlugin, defineUi } from "./app/pages/project-ui.js";
|
|
16
17
|
import { Markdown } from "./markdown/markdown.js";
|
|
17
18
|
import { ActionInputError } from "./app/pages/action-inputs/input-errors.js";
|
|
@@ -22,7 +23,6 @@ import { SignInPrompt } from "./auth/sign-in-prompt.js";
|
|
|
22
23
|
import { useListDisplay } from "./app/displays/display-context.js";
|
|
23
24
|
import { MenuSelect } from "./primitives/menu-select.js";
|
|
24
25
|
import { AuthLayout } from "./auth/auth-layout.js";
|
|
25
|
-
import { OtpInput } from "./primitives/otp-input.js";
|
|
26
26
|
import { allowDelayMs, isFramed, useArmedAfterFocus, useGuardedPress } from "./connect/interaction-guard.js";
|
|
27
27
|
import { ResendCodeButton } from "./auth/resend-code-button.js";
|
|
28
28
|
import { CodeForm } from "./auth/code-form.js";
|
package/dist/package.js
CHANGED