@rockerone/xprnkit 0.3.3 → 0.3.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.
Files changed (32) hide show
  1. package/build/components/identity/index.d.ts +2 -0
  2. package/build/components/identity/index.js +2 -0
  3. package/build/components/identity/xprn-account-list.d.ts +48 -0
  4. package/build/components/identity/xprn-account-list.js +106 -0
  5. package/build/components/identity/xprn-identity-proof-gate.d.ts +67 -0
  6. package/build/components/identity/xprn-identity-proof-gate.js +77 -0
  7. package/build/components/identity/xprn-identity.d.ts +4 -0
  8. package/build/components/identity/xprn-identity.js +5 -4
  9. package/build/components/identity/xprn-session-name.d.ts +23 -2
  10. package/build/components/identity/xprn-session-name.js +48 -3
  11. package/build/components/ui/dropdown.d.ts +3 -1
  12. package/build/components/ui/dropdown.js +4 -1
  13. package/build/components/xprn-transaction.js +3 -1
  14. package/build/global.css +118 -0
  15. package/build/providers/XPRNProvider.d.ts +60 -26
  16. package/build/providers/XPRNProvider.js +324 -267
  17. package/build/services/identity-proof/create-identity-proof.js +1 -0
  18. package/build/services/identity-proof/index.d.ts +5 -1
  19. package/build/services/identity-proof/index.js +3 -0
  20. package/build/services/identity-proof/token-utils.d.ts +48 -0
  21. package/build/services/identity-proof/token-utils.js +85 -0
  22. package/build/services/identity-proof/types.d.ts +25 -2
  23. package/build/services/identity-proof/use-identity-proof.js +5 -3
  24. package/build/services/identity-proof/validate-identity-proof.d.ts +51 -0
  25. package/build/services/identity-proof/validate-identity-proof.js +93 -0
  26. package/build/services/identity-proof/verify-identity-proof.d.ts +4 -4
  27. package/build/services/identity-proof/verify-identity-proof.js +15 -3
  28. package/build/utils/auth-storage.d.ts +126 -0
  29. package/build/utils/auth-storage.js +216 -0
  30. package/build/utils/index.d.ts +1 -0
  31. package/build/utils/index.js +1 -0
  32. package/package.json +2 -1
@@ -2,3 +2,5 @@ export * from './xprn-identity';
2
2
  export * from './xprn-avatar';
3
3
  export * from './xprn-session-actor';
4
4
  export * from './xprn-session-name';
5
+ export * from './xprn-account-list';
6
+ export * from './xprn-identity-proof-gate';
@@ -2,3 +2,5 @@ export * from './xprn-identity';
2
2
  export * from './xprn-avatar';
3
3
  export * from './xprn-session-actor';
4
4
  export * from './xprn-session-name';
5
+ export * from './xprn-account-list';
6
+ export * from './xprn-identity-proof-gate';
@@ -0,0 +1,48 @@
1
+ import * as React from "react";
2
+ import { type SessionStorageEntry } from "../../utils/auth-storage";
3
+ type XPRNAccountListProps = React.HTMLAttributes<HTMLDivElement> & {
4
+ /** Show remove button for each account */
5
+ showRemove?: boolean;
6
+ /** Show add account button */
7
+ showAddAccount?: boolean;
8
+ /** Custom label for add account button */
9
+ addAccountLabel?: string;
10
+ /** Class name for add account button */
11
+ addAccountClassName?: string;
12
+ /** Custom render for each account item */
13
+ renderItem?: (props: AccountItemRenderProps) => React.ReactNode;
14
+ /** Called when an account is selected */
15
+ onSelect?: (entry: SessionStorageEntry) => void;
16
+ /** Called when an account is removed */
17
+ onRemove?: (entry: SessionStorageEntry) => void;
18
+ /** Called when add account is clicked */
19
+ onAddAccount?: () => void;
20
+ /** Class name for account items */
21
+ itemClassName?: string;
22
+ /** Class name for active account item */
23
+ activeClassName?: string;
24
+ };
25
+ type AccountItemRenderProps = {
26
+ entry: SessionStorageEntry;
27
+ isActive: boolean;
28
+ onSelect: () => void;
29
+ onRemove?: () => void;
30
+ };
31
+ /**
32
+ * Component that lists all stored accounts and allows switching between them
33
+ */
34
+ export declare const XPRNAccountList: React.FunctionComponent<XPRNAccountListProps>;
35
+ type XPRNAccountItemProps = {
36
+ entry: SessionStorageEntry;
37
+ isActive: boolean;
38
+ isSwitching?: boolean;
39
+ onSelect: () => void;
40
+ onRemove?: () => void;
41
+ className?: string;
42
+ activeClassName?: string;
43
+ };
44
+ /**
45
+ * Single account item in the list
46
+ */
47
+ export declare const XPRNAccountItem: React.FunctionComponent<XPRNAccountItemProps>;
48
+ export {};
@@ -0,0 +1,106 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import classNames from "classnames";
5
+ import { useXPRN } from "../../providers/XPRNProvider";
6
+ import { sessionStorage } from "../../utils/auth-storage";
7
+ import { XPRNAvatarWrapper, XPRNAvatarImage, XPRNAvatarFallback } from "./xprn-avatar";
8
+ import { cn } from "../../lib/utils";
9
+ /**
10
+ * Component that lists all stored accounts and allows switching between them
11
+ */
12
+ export const XPRNAccountList = ({ className, showRemove = false, showAddAccount = false, addAccountLabel = "Add Account", addAccountClassName, renderItem, onSelect, onRemove, onAddAccount, itemClassName, activeClassName, ...props }) => {
13
+ const { config, session, switchToSession, disconnect, connect } = useXPRN();
14
+ const [entries, setEntries] = React.useState([]);
15
+ const [switching, setSwitching] = React.useState(null);
16
+ const dAppName = config?.requesterAccount ?? '';
17
+ // Load entries on mount and when session changes
18
+ React.useEffect(() => {
19
+ if (typeof window === 'undefined' || !dAppName)
20
+ return;
21
+ const storedEntries = sessionStorage.list(dAppName);
22
+ setEntries(storedEntries);
23
+ }, [session, dAppName]);
24
+ const currentAuth = session ? {
25
+ actor: session.auth.actor.toString(),
26
+ permission: session.auth.permission.toString(),
27
+ } : null;
28
+ const currentChainId = session?.chainId?.toString();
29
+ const isActive = (entry) => {
30
+ if (!currentAuth || !currentChainId)
31
+ return false;
32
+ return (entry.auth.actor === currentAuth.actor &&
33
+ entry.auth.permission === currentAuth.permission &&
34
+ entry.chainId === currentChainId);
35
+ };
36
+ const handleSelect = async (entry) => {
37
+ if (isActive(entry))
38
+ return;
39
+ const authString = `${entry.auth.actor}@${entry.auth.permission}`;
40
+ setSwitching(authString);
41
+ try {
42
+ await switchToSession(authString, entry.chainId);
43
+ onSelect?.(entry);
44
+ }
45
+ catch (error) {
46
+ console.error('Failed to switch session:', error);
47
+ }
48
+ finally {
49
+ setSwitching(null);
50
+ }
51
+ };
52
+ const handleRemove = async (entry) => {
53
+ if (!dAppName)
54
+ return;
55
+ // If removing active session, disconnect first
56
+ if (isActive(entry)) {
57
+ await disconnect();
58
+ }
59
+ // Remove from storage
60
+ sessionStorage.remove(dAppName, entry.auth, entry.chainId);
61
+ // Update local state
62
+ setEntries(prev => prev.filter(e => !(e.auth.actor === entry.auth.actor &&
63
+ e.auth.permission === entry.auth.permission &&
64
+ e.chainId === entry.chainId)));
65
+ onRemove?.(entry);
66
+ };
67
+ const handleAddAccount = () => {
68
+ connect(false);
69
+ onAddAccount?.();
70
+ };
71
+ const rootClasses = classNames({
72
+ "flex flex-col gap-2 bg-black p-2 rounded-lg": true,
73
+ [`${className}`]: className,
74
+ });
75
+ // Show add account button even if no entries
76
+ if (entries.length === 0 && !showAddAccount) {
77
+ return null;
78
+ }
79
+ const addButtonClasses = cn("flex items-center justify-center gap-2 p-2 rounded-lg cursor-pointer transition-colors", "hover:bg-gray-800 text-gray-400", addAccountClassName);
80
+ return (_jsxs("div", { className: rootClasses, ...props, children: [entries.map((entry) => {
81
+ const active = isActive(entry);
82
+ const authString = `${entry.auth.actor}@${entry.auth.permission}`;
83
+ const isSwitching = switching === authString;
84
+ if (renderItem) {
85
+ return (_jsx(React.Fragment, { children: renderItem({
86
+ entry,
87
+ isActive: active,
88
+ onSelect: () => handleSelect(entry),
89
+ onRemove: showRemove ? () => handleRemove(entry) : undefined,
90
+ }) }, authString));
91
+ }
92
+ return (_jsx(XPRNAccountItem, { entry: entry, isActive: active, isSwitching: isSwitching, onSelect: () => handleSelect(entry), onRemove: showRemove ? () => handleRemove(entry) : undefined, className: itemClassName, activeClassName: activeClassName }, authString));
93
+ }), showAddAccount && (_jsxs("button", { type: "button", className: addButtonClasses, onClick: handleAddAccount, children: [_jsxs("svg", { className: "h-5 w-5", xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("path", { d: "M12 5v14" }), _jsx("path", { d: "M5 12h14" })] }), addAccountLabel] }))] }));
94
+ };
95
+ /**
96
+ * Single account item in the list
97
+ */
98
+ export const XPRNAccountItem = ({ entry, isActive, isSwitching, onSelect, onRemove, className, activeClassName, }) => {
99
+ const itemClasses = cn("flex items-center gap-3 p-2 rounded-lg cursor-pointer transition-colors text-white", "hover:bg-gray-800", isActive && "bg-gray-900", isActive && activeClassName, className);
100
+ return (_jsxs("div", { className: itemClasses, onClick: onSelect, children: [_jsxs(XPRNAvatarWrapper, { className: "w-10 h-10 shrink-0", children: [_jsx(XPRNAvatarFallback, { children: entry.auth.actor[0]?.toUpperCase() }), entry.profile?.avatar && (_jsx(XPRNAvatarImage, { src: `data:image/png;base64,${entry.profile.avatar}` }))] }), _jsxs("div", { className: "flex-1 min-w-0", children: [_jsxs("div", { className: "font-medium truncate flex items-center gap-1", children: [entry.profile?.displayName || entry.auth.actor, entry.profile?.isKyc && (_jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "text-gray-300 shrink-0", children: [_jsx("path", { d: "M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z" }), _jsx("path", { d: "m9 12 2 2 4-4" })] })), entry.identityProofToken && (_jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "text-white shrink-0", children: [_jsx("path", { d: "M16 10h2" }), _jsx("path", { d: "M16 14h2" }), _jsx("path", { d: "M6.17 15a3 3 0 0 1 5.66 0" }), _jsx("circle", { cx: "9", cy: "11", r: "2" }), _jsx("rect", { x: "2", y: "5", width: "20", height: "14", rx: "2" })] }))] }), _jsxs("div", { className: "text-sm text-gray-300 truncate", children: [entry.auth.actor, "@", entry.auth.permission] })] }), isSwitching && (_jsxs("svg", { className: "animate-spin h-5 w-5 text-gray-400", xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24", children: [_jsx("circle", { className: "opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4" }), _jsx("path", { className: "opacity-75", fill: "currentColor", d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" })] })), isActive && !isSwitching && (_jsx("svg", { className: "h-5 w-5 text-green-500", xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: _jsx("path", { d: "M20 6 9 17l-5-5" }) })), onRemove && !isActive && (_jsx("button", { onClick: (e) => {
101
+ e.stopPropagation();
102
+ onRemove();
103
+ }, className: "p-1 hover:bg-gray-700 rounded", title: "Remove account", children: _jsxs("svg", { className: "h-4 w-4 text-gray-400", xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("path", { d: "M18 6 6 18" }), _jsx("path", { d: "m6 6 12 12" })] }) }))] }));
104
+ };
105
+ XPRNAccountList.displayName = "XPRNAccountList";
106
+ XPRNAccountItem.displayName = "XPRNAccountItem";
@@ -0,0 +1,67 @@
1
+ import * as React from "react";
2
+ type XPRNIdentityProofGateProps = {
3
+ /**
4
+ * Content to render when identity proof is obtained
5
+ */
6
+ children: React.ReactNode;
7
+ /**
8
+ * Content to render when identity proof is required but not yet obtained.
9
+ * If not provided, renders nothing.
10
+ */
11
+ fallback?: React.ReactNode;
12
+ /**
13
+ * Content to render while identity proof is in progress (signing/verifying).
14
+ * If not provided, uses fallback.
15
+ */
16
+ loading?: React.ReactNode;
17
+ /**
18
+ * Content to render when identity proof failed.
19
+ * If not provided, uses fallback.
20
+ */
21
+ error?: React.ReactNode;
22
+ /**
23
+ * Content to render when no session is connected.
24
+ * If not provided, uses fallback.
25
+ */
26
+ notConnected?: React.ReactNode;
27
+ /**
28
+ * If true, only gates when identityProof.required is true in config.
29
+ * If false (default), gates whenever identity proof is configured.
30
+ */
31
+ onlyWhenRequired?: boolean;
32
+ };
33
+ /**
34
+ * Conditionally renders children based on identity proof status.
35
+ *
36
+ * Use this component to gate access to features that require identity proof.
37
+ * When identity proof is not obtained, shows fallback content (or nothing).
38
+ *
39
+ * @example
40
+ * ```tsx
41
+ * <XPRNIdentityProofGate
42
+ * fallback={<button onClick={() => requestIdentityProof(...)}>Verify Identity</button>}
43
+ * loading={<span>Verifying...</span>}
44
+ * >
45
+ * <ProtectedFeature />
46
+ * </XPRNIdentityProofGate>
47
+ * ```
48
+ */
49
+ export declare const XPRNIdentityProofGate: React.FunctionComponent<XPRNIdentityProofGateProps>;
50
+ /**
51
+ * Hook to check identity proof gate status
52
+ * Returns the same information used by XPRNIdentityProofGate
53
+ */
54
+ export declare function useIdentityProofGate(options?: {
55
+ onlyWhenRequired?: boolean;
56
+ }): {
57
+ isGateActive: boolean;
58
+ isConnected: boolean;
59
+ isInProgress: boolean;
60
+ hasError: boolean;
61
+ isVerified: boolean;
62
+ shouldRenderChildren: boolean;
63
+ needsIdentityProof: boolean;
64
+ requestIdentityProof: (success: (res: import("../../providers/XPRNProvider").XPRNIdentityProof) => void, fail: (e: any) => void) => void;
65
+ identityProofStatus: import("../..").IdentityProofStatus;
66
+ };
67
+ export {};
@@ -0,0 +1,77 @@
1
+ "use client";
2
+ import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
3
+ import { useXPRN } from "../../providers/XPRNProvider";
4
+ /**
5
+ * Conditionally renders children based on identity proof status.
6
+ *
7
+ * Use this component to gate access to features that require identity proof.
8
+ * When identity proof is not obtained, shows fallback content (or nothing).
9
+ *
10
+ * @example
11
+ * ```tsx
12
+ * <XPRNIdentityProofGate
13
+ * fallback={<button onClick={() => requestIdentityProof(...)}>Verify Identity</button>}
14
+ * loading={<span>Verifying...</span>}
15
+ * >
16
+ * <ProtectedFeature />
17
+ * </XPRNIdentityProofGate>
18
+ * ```
19
+ */
20
+ export const XPRNIdentityProofGate = ({ children, fallback = null, loading, error, notConnected, onlyWhenRequired = false, }) => {
21
+ const { session, identityProof, identityProofStatus, isIdentityProofEnabled, isIdentityProofRequired, } = useXPRN();
22
+ // If identity proof is not enabled, render children directly
23
+ if (!isIdentityProofEnabled) {
24
+ return _jsx(_Fragment, { children: children });
25
+ }
26
+ // If onlyWhenRequired is true and identity proof is not required, render children
27
+ if (onlyWhenRequired && !isIdentityProofRequired) {
28
+ return _jsx(_Fragment, { children: children });
29
+ }
30
+ // No session connected
31
+ if (!session) {
32
+ return _jsx(_Fragment, { children: notConnected ?? fallback });
33
+ }
34
+ // Identity proof in progress
35
+ if (identityProofStatus === "signing" || identityProofStatus === "verifying" || identityProofStatus === "validating") {
36
+ return _jsx(_Fragment, { children: loading ?? fallback });
37
+ }
38
+ // Identity proof failed
39
+ if (identityProofStatus === "error" || identityProofStatus === "expired") {
40
+ return _jsx(_Fragment, { children: error ?? fallback });
41
+ }
42
+ // Identity proof obtained
43
+ if (identityProof) {
44
+ return _jsx(_Fragment, { children: children });
45
+ }
46
+ // Identity proof needed but not obtained
47
+ return _jsx(_Fragment, { children: fallback });
48
+ };
49
+ XPRNIdentityProofGate.displayName = "XPRNIdentityProofGate";
50
+ /**
51
+ * Hook to check identity proof gate status
52
+ * Returns the same information used by XPRNIdentityProofGate
53
+ */
54
+ export function useIdentityProofGate(options) {
55
+ const { session, identityProof, identityProofStatus, isIdentityProofEnabled, isIdentityProofRequired, needsIdentityProof, requestIdentityProof, } = useXPRN();
56
+ const onlyWhenRequired = options?.onlyWhenRequired ?? false;
57
+ // Determine if gating should be active
58
+ const isGateActive = isIdentityProofEnabled && (!onlyWhenRequired || isIdentityProofRequired);
59
+ // Determine current state
60
+ const isConnected = session !== null;
61
+ const isInProgress = identityProofStatus === "signing" || identityProofStatus === "verifying" || identityProofStatus === "validating";
62
+ const hasError = identityProofStatus === "error" || identityProofStatus === "expired";
63
+ const isVerified = identityProof !== null;
64
+ // Should render protected content?
65
+ const shouldRenderChildren = !isGateActive || isVerified;
66
+ return {
67
+ isGateActive,
68
+ isConnected,
69
+ isInProgress,
70
+ hasError,
71
+ isVerified,
72
+ shouldRenderChildren,
73
+ needsIdentityProof: isGateActive && needsIdentityProof,
74
+ requestIdentityProof,
75
+ identityProofStatus,
76
+ };
77
+ }
@@ -4,6 +4,10 @@ type XPRNIdentityProps = React.HTMLAttributes<HTMLDivElement> & {
4
4
  activeSessionClassName?: string;
5
5
  dropdownClassName?: string;
6
6
  avatarClassName?: string;
7
+ /** Match dropdown width to trigger width */
8
+ matchDropdownWidth?: boolean;
9
+ /** Close dropdown when children are clicked */
10
+ closeOnSelect?: boolean;
7
11
  };
8
12
  export declare const XPRNIdentity: React.FunctionComponent<XPRNIdentityProps>;
9
13
  export {};
@@ -8,7 +8,8 @@ import { XPRNSessionName } from "./xprn-session-name";
8
8
  import { useXPRN } from "../../providers/XPRNProvider";
9
9
  import { XPRNConnectButton } from "./../xprn-session";
10
10
  import { DropdownMenu, DropdownMenuContent, DropdownMenuTrigger, } from "./../ui/dropdown";
11
- export const XPRNIdentity = ({ children, className, showLogout, activeSessionClassName, dropdownClassName, avatarClassName, }) => {
11
+ export const XPRNIdentity = ({ children, className, showLogout, activeSessionClassName, dropdownClassName, avatarClassName, matchDropdownWidth = true, closeOnSelect = true, }) => {
12
+ const [open, setOpen] = React.useState(false);
12
13
  const rootClasses = classNames({
13
14
  "grid grid-cols-[min-content,1fr] gap-4 items-center inline": true,
14
15
  [`${className}`]: className,
@@ -18,13 +19,13 @@ export const XPRNIdentity = ({ children, className, showLogout, activeSessionCla
18
19
  [`${activeSessionClassName}`]: activeSessionClassName,
19
20
  });
20
21
  const dropdownClasses = classNames({
21
- "flex flex-col items-center gap-2": true,
22
+ "flex flex-col gap-2": true,
22
23
  [`${dropdownClassName}`]: dropdownClassName,
23
24
  });
24
- const { session, profile, disconnect, authentication } = useXPRN();
25
+ const { session, profile, disconnect } = useXPRN();
25
26
  const handleDisconnect = React.useCallback((e) => {
26
27
  e.preventDefault();
27
28
  disconnect();
28
29
  }, [disconnect]);
29
- return (_jsx(_Fragment, { children: session ? (_jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { className: "inline w-auto flex-grow-0", children: _jsxs("div", { className: activeSessionClasses, children: [_jsx("div", { className: "w-10 h-10", children: _jsx(XPRNAvatar, { className: avatarClassName }) }), _jsxs("div", { className: "flex flex-col", children: [_jsx(XPRNSessionName, { className: "text-md font-bold" }), _jsxs("div", { className: "flex gap-2", children: [_jsx(XPRNSessionActor, { className: "text-md" }), showLogout && (_jsx("a", { href: "#", className: "underline", onClick: e => handleDisconnect(e), children: "Log out" }))] })] })] }) }), children && (_jsx(DropdownMenuContent, { className: dropdownClasses, children: children }))] })) : (_jsx(XPRNConnectButton, { className: rootClasses, children: "Connect" })) }));
30
+ return (_jsx(_Fragment, { children: session ? (_jsxs(DropdownMenu, { open: open, onOpenChange: setOpen, children: [_jsx(DropdownMenuTrigger, { className: "inline w-auto flex-grow-0", children: _jsxs("div", { className: activeSessionClasses, children: [_jsx("div", { className: "w-10 h-10", children: _jsx(XPRNAvatar, { className: avatarClassName }) }), _jsxs("div", { className: "flex flex-col", children: [_jsx(XPRNSessionName, { className: "text-md font-bold" }), _jsxs("div", { className: "flex gap-2", children: [_jsx(XPRNSessionActor, { className: "text-md" }), showLogout && (_jsx("a", { href: "#", className: "underline", onClick: e => handleDisconnect(e), children: "Log out" }))] })] })] }) }), children && (_jsx(DropdownMenuContent, { className: dropdownClasses, matchTriggerWidth: matchDropdownWidth, onClick: () => closeOnSelect && setOpen(false), children: children }))] })) : (_jsx(XPRNConnectButton, { className: rootClasses, children: "Connect" })) }));
30
31
  };
@@ -1,4 +1,25 @@
1
1
  import React from "react";
2
- type XPRNSessionNameProps = React.HTMLAttributes<HTMLHeadElement> & {};
2
+ type XPRNSessionNameProps = React.HTMLAttributes<HTMLHeadElement> & {
3
+ /** Show identity proof badge with status indicator */
4
+ showIdentityProof?: boolean;
5
+ /** @deprecated Use showIdentityProof instead */
6
+ showAuthentication?: boolean;
7
+ };
8
+ /**
9
+ * Get badge styling based on identity proof status
10
+ */
11
+ declare function useIdentityProofBadgeStyle(): {
12
+ visible: boolean;
13
+ className: string;
14
+ isPending?: undefined;
15
+ isSuccess?: undefined;
16
+ isError?: undefined;
17
+ } | {
18
+ visible: boolean;
19
+ className: string;
20
+ isPending: boolean;
21
+ isSuccess: boolean;
22
+ isError: boolean;
23
+ };
3
24
  export declare const XPRNSessionName: React.FunctionComponent<XPRNSessionNameProps>;
4
- export {};
25
+ export { useIdentityProofBadgeStyle };
@@ -1,14 +1,59 @@
1
1
  "use client";
2
2
  import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useMemo } from "react";
3
4
  import classNames from "classnames";
4
5
  import { useXPRN } from "../../providers/XPRNProvider";
6
+ /**
7
+ * Get badge styling based on identity proof status
8
+ */
9
+ function useIdentityProofBadgeStyle() {
10
+ const { identityProofStatus, isIdentityProofEnabled, needsIdentityProof } = useXPRN();
11
+ return useMemo(() => {
12
+ if (!isIdentityProofEnabled) {
13
+ return { visible: false, className: "" };
14
+ }
15
+ const isPending = identityProofStatus === "signing" ||
16
+ identityProofStatus === "verifying" ||
17
+ identityProofStatus === "validating";
18
+ const isSuccess = identityProofStatus === "success";
19
+ const isError = identityProofStatus === "error" || identityProofStatus === "expired";
20
+ const isIdle = identityProofStatus === "idle";
21
+ // Build className based on status
22
+ let className = "";
23
+ if (isPending) {
24
+ className = "animate-pulse text-gray-400";
25
+ }
26
+ else if (isSuccess) {
27
+ className = "text-white";
28
+ }
29
+ else if (isError) {
30
+ className = "text-red-500";
31
+ }
32
+ else if (isIdle && needsIdentityProof) {
33
+ className = "text-gray-500";
34
+ }
35
+ else {
36
+ className = "text-white";
37
+ }
38
+ return {
39
+ visible: true,
40
+ className,
41
+ isPending,
42
+ isSuccess,
43
+ isError,
44
+ };
45
+ }, [identityProofStatus, isIdentityProofEnabled, needsIdentityProof]);
46
+ }
5
47
  export const XPRNSessionName = ({ children, className, }) => {
6
- const { profile, authentication } = useXPRN();
48
+ const { profile, session, config } = useXPRN();
49
+ const badgeStyle = useIdentityProofBadgeStyle();
50
+ const showBadge = (config?.identityProof?.required || config?.identityProof?.enforceOnConnect) && badgeStyle.visible;
7
51
  const rootClasses = classNames({
8
52
  "flex gap-2 items-center justify-center": true,
9
53
  [`${className}`]: className,
10
54
  });
11
- if (!profile)
55
+ if (!profile || !session)
12
56
  return _jsx(_Fragment, {});
13
- return (_jsxs("div", { className: `${rootClasses}`, children: [_jsxs("h3", { children: ["@", profile.displayName.toString()] }), _jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "lucide lucide-badge-check", children: [_jsx("path", { d: "M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z" }), _jsx("path", { d: "m9 12 2 2 4-4" })] }), authentication && (_jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "lucide lucide-id-card", children: [_jsx("path", { d: "M16 10h2" }), _jsx("path", { d: "M16 14h2" }), _jsx("path", { d: "M6.17 15a3 3 0 0 1 5.66 0" }), _jsx("circle", { cx: "9", cy: "11", r: "2" }), _jsx("rect", { x: "2", y: "5", width: "20", height: "14", rx: "2" })] }))] }));
57
+ return (_jsxs("div", { className: `${rootClasses}`, children: [_jsx("h3", { children: profile.displayName ? profile.displayName.toString() : session.auth.actor.toString() }), profile.isKyc && (_jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "lucide lucide-badge-check", children: [_jsx("path", { d: "M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z" }), _jsx("path", { d: "m9 12 2 2 4-4" })] })), showBadge && (_jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: classNames("lucide lucide-id-card", badgeStyle.className), children: [_jsx("path", { d: "M16 10h2" }), _jsx("path", { d: "M16 14h2" }), _jsx("path", { d: "M6.17 15a3 3 0 0 1 5.66 0" }), _jsx("circle", { cx: "9", cy: "11", r: "2" }), _jsx("rect", { x: "2", y: "5", width: "20", height: "14", rx: "2" })] }))] }));
14
58
  };
59
+ export { useIdentityProofBadgeStyle };
@@ -10,7 +10,9 @@ declare const DropdownMenuSubTrigger: React.ForwardRefExoticComponent<Omit<Dropd
10
10
  inset?: boolean;
11
11
  } & React.RefAttributes<HTMLDivElement>>;
12
12
  declare const DropdownMenuSubContent: React.ForwardRefExoticComponent<Omit<DropdownMenuPrimitive.DropdownMenuSubContentProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
13
- declare const DropdownMenuContent: React.ForwardRefExoticComponent<Omit<DropdownMenuPrimitive.DropdownMenuContentProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
13
+ declare const DropdownMenuContent: React.ForwardRefExoticComponent<Omit<DropdownMenuPrimitive.DropdownMenuContentProps & React.RefAttributes<HTMLDivElement>, "ref"> & {
14
+ matchTriggerWidth?: boolean;
15
+ } & React.RefAttributes<HTMLDivElement>>;
14
16
  declare const DropdownMenuItem: React.ForwardRefExoticComponent<Omit<DropdownMenuPrimitive.DropdownMenuItemProps & React.RefAttributes<HTMLDivElement>, "ref"> & {
15
17
  inset?: boolean;
16
18
  } & React.RefAttributes<HTMLDivElement>>;
@@ -16,7 +16,10 @@ DropdownMenuSubTrigger.displayName =
16
16
  const DropdownMenuSubContent = React.forwardRef(({ className, ...props }, ref) => (_jsx(DropdownMenuPrimitive.SubContent, { ref: ref, className: cn("z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", className), ...props })));
17
17
  DropdownMenuSubContent.displayName =
18
18
  DropdownMenuPrimitive.SubContent.displayName;
19
- const DropdownMenuContent = React.forwardRef(({ className, sideOffset = 4, ...props }, ref) => (_jsx(DropdownMenuPrimitive.Portal, { children: _jsx(DropdownMenuPrimitive.Content, { ref: ref, sideOffset: sideOffset, className: cn("z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md", "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", className), ...props }) })));
19
+ const DropdownMenuContent = React.forwardRef(({ className, sideOffset = 4, matchTriggerWidth = false, style, ...props }, ref) => (_jsx(DropdownMenuPrimitive.Portal, { children: _jsx(DropdownMenuPrimitive.Content, { ref: ref, sideOffset: sideOffset, className: cn("z-50 overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md", !matchTriggerWidth && "min-w-[8rem]", "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", className), style: {
20
+ ...style,
21
+ ...(matchTriggerWidth && { width: 'var(--radix-dropdown-menu-trigger-width)' }),
22
+ }, ...props }) })));
20
23
  DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;
21
24
  const DropdownMenuItem = React.forwardRef(({ className, inset, ...props }, ref) => (_jsx(DropdownMenuPrimitive.Item, { ref: ref, className: cn("relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50", inset && "pl-8", className), ...props })));
22
25
  DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;
@@ -14,7 +14,7 @@ const XPRNTransactionVariants = cva("inline-flex items-center justify-center whi
14
14
  });
15
15
  const XPRNTransaction = React.forwardRef(({ className, variant, size, asChild = false, onClick, children, actions, onTransactionStart, onTransactionSuccess, onTransactionFail, ...props }, ref) => {
16
16
  const Comp = asChild ? Slot : "button";
17
- const { session, connect, addTransactionError, txErrorsStack } = useXPRN();
17
+ const { session, connect, addTransactionError } = useXPRN();
18
18
  const [txStatus, setTxStatus] = React.useState('idle');
19
19
  const TxStatusNode = React.useMemo(() => {
20
20
  if (txStatus == 'idle')
@@ -37,12 +37,14 @@ const XPRNTransaction = React.forwardRef(({ className, variant, size, asChild =
37
37
  onTransactionSuccess(res);
38
38
  setTxStatus('success');
39
39
  }).catch((e) => {
40
+ console.error(e);
40
41
  setTxStatus('fail');
41
42
  addTransactionError(e);
42
43
  });
43
44
  }
44
45
  catch (e) {
45
46
  setTxStatus('fail');
47
+ console.error(e);
46
48
  addTransactionError(e.toString());
47
49
  if (onTransactionFail)
48
50
  onTransactionFail(e);