@crossmint/client-sdk-react-ui 1.7.1 → 1.9.1

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 (52) hide show
  1. package/dist/index.cjs +1 -1
  2. package/dist/index.d.cts +20 -7
  3. package/dist/index.d.ts +20 -7
  4. package/dist/index.js +1 -1
  5. package/package.json +17 -7
  6. package/src/components/auth/AuthForm.tsx +50 -0
  7. package/src/components/auth/AuthFormBackButton.tsx +26 -0
  8. package/src/components/auth/AuthFormDialog.tsx +33 -0
  9. package/src/components/auth/EmbeddedAuthForm.tsx +5 -0
  10. package/src/components/auth/methods/email/EmailAuthFlow.tsx +19 -0
  11. package/src/components/auth/methods/email/EmailOTPInput.tsx +123 -0
  12. package/src/components/auth/methods/email/EmailSignIn.tsx +113 -0
  13. package/src/components/auth/methods/farcaster/FarcasterSignIn.tsx +170 -0
  14. package/src/components/auth/methods/google/GoogleSignIn.tsx +62 -0
  15. package/src/components/common/Dialog.tsx +141 -0
  16. package/src/components/common/Divider.tsx +25 -0
  17. package/src/components/common/InputOTP.tsx +89 -0
  18. package/src/components/common/PoweredByCrossmint.tsx +4 -9
  19. package/src/components/common/Spinner.tsx +22 -0
  20. package/src/components/dynamic-xyz/DynamicContextProviderWrapper.tsx +31 -0
  21. package/src/components/embed/v3/CrossmintEmbeddedCheckoutV3.tsx +7 -0
  22. package/src/components/embed/v3/EmbeddedCheckoutV3IFrame.tsx +74 -0
  23. package/src/components/embed/v3/crypto/CryptoWalletConnectionHandler.tsx +138 -0
  24. package/src/components/embed/v3/crypto/utils/handleEvmTransaction.ts +65 -0
  25. package/src/components/embed/v3/crypto/utils/handleSendTransaction.ts +31 -0
  26. package/src/components/embed/v3/crypto/utils/handleSolanaTransaction.ts +51 -0
  27. package/src/components/embed/v3/index.ts +1 -0
  28. package/src/components/index.ts +3 -0
  29. package/src/hooks/index.ts +1 -0
  30. package/src/hooks/useAuthSignIn.ts +117 -0
  31. package/src/hooks/useCrossmintCheckout.tsx +54 -0
  32. package/src/hooks/useOAuthWindowListener.ts +87 -0
  33. package/src/hooks/useRefreshToken.test.ts +21 -8
  34. package/src/hooks/useRefreshToken.ts +5 -4
  35. package/src/icons/alert.tsx +19 -0
  36. package/src/icons/discord.tsx +18 -0
  37. package/src/icons/emailOTP.tsx +147 -0
  38. package/src/icons/farcaster.tsx +26 -0
  39. package/src/icons/google.tsx +30 -0
  40. package/src/icons/leftArrow.tsx +20 -0
  41. package/src/icons/poweredByLeaf.tsx +2 -2
  42. package/src/providers/CrossmintAuthProvider.test.tsx +4 -3
  43. package/src/providers/CrossmintAuthProvider.tsx +36 -32
  44. package/src/providers/CrossmintWalletProvider.tsx +3 -3
  45. package/src/providers/auth/AuthFormProvider.test.tsx +105 -0
  46. package/src/providers/auth/AuthFormProvider.tsx +116 -0
  47. package/src/providers/auth/FarcasterProvider.tsx +12 -0
  48. package/src/twind.config.ts +101 -1
  49. package/src/types/auth.ts +4 -0
  50. package/src/utils/authCookies.ts +0 -3
  51. package/src/utils/createCrossmintApiClient.ts +17 -0
  52. package/src/components/auth/AuthModal.tsx +0 -207
@@ -1,21 +1,26 @@
1
- import { REFRESH_TOKEN_PREFIX, SESSION_PREFIX, deleteCookie, getCookie, setCookie } from "@/utils/authCookies";
2
1
  import { type ReactNode, createContext, useEffect, useState } from "react";
3
- import { createPortal } from "react-dom";
4
2
 
5
3
  import { CrossmintAuthService } from "@crossmint/client-sdk-auth";
6
4
  import type { EVMSmartWalletChain } from "@crossmint/client-sdk-smart-wallet";
7
5
  import { type UIConfig, validateApiKeyAndGetCrossmintBaseUrl } from "@crossmint/common-sdk-base";
8
-
9
- import AuthModal from "../components/auth/AuthModal";
6
+ import {
7
+ SESSION_PREFIX,
8
+ REFRESH_TOKEN_PREFIX,
9
+ type AuthMaterialWithUser,
10
+ type SDKExternalUser,
11
+ } from "@crossmint/common-sdk-auth";
12
+
13
+ import AuthFormDialog from "../components/auth/AuthFormDialog";
10
14
  import { useCrossmint, useRefreshToken, useWallet } from "../hooks";
11
15
  import { CrossmintWalletProvider } from "./CrossmintWalletProvider";
12
- import type { AuthMaterial, SDKExternalUser } from "@crossmint/common-sdk-auth";
16
+ import { deleteCookie, getCookie, setCookie } from "@/utils/authCookies";
17
+ import { AuthFormProvider } from "./auth/AuthFormProvider";
13
18
 
14
19
  export type CrossmintAuthWalletConfig = {
15
20
  defaultChain: EVMSmartWalletChain;
16
21
  createOnLogin: "all-users" | "off";
17
22
  type: "evm-smart-wallet";
18
- showWalletModals?: boolean;
23
+ showPasskeyHelpers?: boolean;
19
24
  };
20
25
 
21
26
  export type LoginMethod = "email" | "google" | "farcaster";
@@ -64,12 +69,12 @@ export function CrossmintAuthProvider({
64
69
  );
65
70
  const crossmintAuthService = new CrossmintAuthService(crossmint.apiKey);
66
71
  const crossmintBaseUrl = validateApiKeyAndGetCrossmintBaseUrl(crossmint.apiKey);
67
- const [modalOpen, setModalOpen] = useState(false);
72
+ const [dialogOpen, setDialogOpen] = useState(false);
68
73
 
69
- const setAuthMaterial = (authMaterial: AuthMaterial) => {
70
- setCookie(SESSION_PREFIX, authMaterial.jwtToken);
74
+ const setAuthMaterial = (authMaterial: AuthMaterialWithUser) => {
75
+ setCookie(SESSION_PREFIX, authMaterial.jwt);
71
76
  setCookie(REFRESH_TOKEN_PREFIX, authMaterial.refreshToken.secret, authMaterial.refreshToken.expiresAt);
72
- setJwt(authMaterial.jwtToken);
77
+ setJwt(authMaterial.jwt);
73
78
  setRefreshToken(authMaterial.refreshToken.secret);
74
79
  setUser(authMaterial.user);
75
80
  };
@@ -96,7 +101,7 @@ export function CrossmintAuthProvider({
96
101
  return;
97
102
  }
98
103
 
99
- setModalOpen(false);
104
+ setDialogOpen(false);
100
105
  }, [crossmint.jwt]);
101
106
 
102
107
  const login = () => {
@@ -105,20 +110,20 @@ export function CrossmintAuthProvider({
105
110
  return;
106
111
  }
107
112
 
108
- setModalOpen(true);
113
+ setDialogOpen(true);
109
114
  };
110
115
 
111
116
  const getAuthStatus = (): AuthStatus => {
112
117
  if (crossmint.jwt != null) {
113
118
  return "logged-in";
114
119
  }
115
- if (modalOpen) {
120
+ if (dialogOpen) {
116
121
  return "in-progress";
117
122
  }
118
123
  return "logged-out";
119
124
  };
120
125
 
121
- const fetchAuthMaterial = async (refreshToken: string): Promise<AuthMaterial> => {
126
+ const fetchAuthMaterial = async (refreshToken: string): Promise<AuthMaterialWithUser> => {
122
127
  const authMaterial = await crossmintAuthService.refreshAuthMaterial(refreshToken);
123
128
  setAuthMaterial(authMaterial);
124
129
  return authMaterial;
@@ -148,26 +153,25 @@ export function CrossmintAuthProvider({
148
153
  >
149
154
  <CrossmintWalletProvider
150
155
  defaultChain={embeddedWallets.defaultChain}
151
- showWalletModals={embeddedWallets.showWalletModals}
156
+ showPasskeyHelpers={embeddedWallets.showPasskeyHelpers}
152
157
  appearance={appearance}
153
158
  >
154
- <WalletManager embeddedWallets={embeddedWallets} accessToken={crossmint.jwt}>
155
- {children}
156
- </WalletManager>
157
- {modalOpen
158
- ? createPortal(
159
- <AuthModal
160
- baseUrl={crossmintBaseUrl}
161
- setModalOpen={setModalOpen}
162
- fetchAuthMaterial={fetchAuthMaterial}
163
- apiKey={crossmint.apiKey}
164
- appearance={appearance}
165
- loginMethods={loginMethods}
166
- />,
167
-
168
- document.body
169
- )
170
- : null}
159
+ <AuthFormProvider
160
+ initialState={{
161
+ apiKey: crossmint.apiKey,
162
+ baseUrl: crossmintBaseUrl,
163
+ fetchAuthMaterial,
164
+ appearance,
165
+ setDialogOpen,
166
+ loginMethods,
167
+ }}
168
+ >
169
+ <WalletManager embeddedWallets={embeddedWallets} accessToken={crossmint.jwt}>
170
+ {children}
171
+ </WalletManager>
172
+
173
+ <AuthFormDialog open={dialogOpen} />
174
+ </AuthFormProvider>
171
175
  </CrossmintWalletProvider>
172
176
  </AuthContext.Provider>
173
177
  );
@@ -57,12 +57,12 @@ export type WalletConfig = WalletParams & { type: "evm-smart-wallet" };
57
57
  export function CrossmintWalletProvider({
58
58
  children,
59
59
  defaultChain,
60
- showWalletModals = true, // enabled by default
60
+ showPasskeyHelpers = true, // enabled by default
61
61
  appearance,
62
62
  }: {
63
63
  children: ReactNode;
64
64
  defaultChain: EVMSmartWalletChain;
65
- showWalletModals?: boolean;
65
+ showPasskeyHelpers?: boolean;
66
66
  appearance?: UIConfig;
67
67
  }) {
68
68
  const { crossmint } = useCrossmint("CrossmintWalletProvider must be used within CrossmintProvider");
@@ -99,7 +99,7 @@ export function CrossmintWalletProvider({
99
99
  };
100
100
 
101
101
  const enhanceConfigWithPasskeyPrompts = (config: WalletConfig) => {
102
- if (showWalletModals && (config.signer as PasskeySigner).type === "PASSKEY") {
102
+ if (showPasskeyHelpers && (config.signer as PasskeySigner).type === "PASSKEY") {
103
103
  return {
104
104
  ...config,
105
105
  signer: {
@@ -0,0 +1,105 @@
1
+ import { render, fireEvent, waitFor } from "@testing-library/react";
2
+ import { AuthFormProvider, useAuthForm } from "./AuthFormProvider";
3
+ import { describe, expect, test, vi } from "vitest";
4
+ import type { LoginMethod } from "..";
5
+
6
+ // Mock component to test the AuthFormProvider
7
+ function TestComponent() {
8
+ const { step, apiKey, baseUrl, loginMethods, setStep, setDialogOpen, oauthUrlMap, isLoadingOauthUrlMap } =
9
+ useAuthForm();
10
+
11
+ return (
12
+ <div>
13
+ <div data-testid="step">{step}</div>
14
+ <div data-testid="api-key">{apiKey}</div>
15
+ <div data-testid="base-url">{baseUrl}</div>
16
+ <div data-testid="login-methods">{JSON.stringify(loginMethods)}</div>
17
+ <button onClick={() => setStep("otp")} data-testid="set-step">
18
+ Set Step to OTP
19
+ </button>
20
+ <button onClick={() => setDialogOpen(true)} data-testid="set-dialog-open">
21
+ Open Dialog
22
+ </button>
23
+ <div data-testid="oauth-url">{JSON.stringify(oauthUrlMap)}</div>
24
+ <div data-testid="is-loading-oauth-url">{isLoadingOauthUrlMap.toString()}</div>
25
+ </div>
26
+ );
27
+ }
28
+
29
+ describe("AuthFormProvider", () => {
30
+ const mockFetchAuthMaterial = vi.fn().mockResolvedValue({});
31
+ const mockInitialState = {
32
+ apiKey: "test-api-key",
33
+ baseUrl: "https://api.example.com",
34
+ fetchAuthMaterial: mockFetchAuthMaterial,
35
+ loginMethods: ["email", "google"] as LoginMethod[],
36
+ setDialogOpen: vi.fn(),
37
+ };
38
+
39
+ beforeEach(() => {
40
+ vi.resetAllMocks();
41
+ global.fetch = vi.fn().mockResolvedValue({
42
+ ok: true,
43
+ json: () => Promise.resolve({ oauthUrl: "https://oauth.example.com" }),
44
+ });
45
+ });
46
+
47
+ test("provides initial context values and fetches OAuth URLs", async () => {
48
+ const { getByTestId } = render(
49
+ <AuthFormProvider initialState={mockInitialState}>
50
+ <TestComponent />
51
+ </AuthFormProvider>
52
+ );
53
+
54
+ expect(getByTestId("step").textContent).toBe("initial");
55
+ expect(getByTestId("api-key").textContent).toBe("test-api-key");
56
+ expect(getByTestId("base-url").textContent).toBe("https://api.example.com");
57
+ expect(getByTestId("login-methods").textContent).toBe('["email","google"]');
58
+
59
+ await waitFor(() => {
60
+ expect(getByTestId("oauth-url").textContent).toBe('{"google":"https://oauth.example.com"}');
61
+ expect(getByTestId("is-loading-oauth-url").textContent).toBe("false");
62
+ });
63
+ });
64
+
65
+ test("updates step", () => {
66
+ const { getByTestId } = render(
67
+ <AuthFormProvider initialState={mockInitialState}>
68
+ <TestComponent />
69
+ </AuthFormProvider>
70
+ );
71
+
72
+ fireEvent.click(getByTestId("set-step"));
73
+ expect(getByTestId("step").textContent).toBe("otp");
74
+ });
75
+
76
+ test("calls setDialogOpen", () => {
77
+ const { getByTestId } = render(
78
+ <AuthFormProvider initialState={mockInitialState}>
79
+ <TestComponent />
80
+ </AuthFormProvider>
81
+ );
82
+
83
+ fireEvent.click(getByTestId("set-dialog-open"));
84
+ expect(mockInitialState.setDialogOpen).toHaveBeenCalledWith(true);
85
+ });
86
+
87
+ test("handles OAuth URL fetch error", async () => {
88
+ global.fetch = vi.fn().mockRejectedValue(new Error("Fetch failed"));
89
+ const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {});
90
+
91
+ const { getByTestId } = render(
92
+ <AuthFormProvider initialState={mockInitialState}>
93
+ <TestComponent />
94
+ </AuthFormProvider>
95
+ );
96
+
97
+ await waitFor(() => {
98
+ expect(getByTestId("oauth-url").textContent).toBe('{"google":""}');
99
+ expect(getByTestId("is-loading-oauth-url").textContent).toBe("false");
100
+ });
101
+
102
+ expect(consoleSpy).toHaveBeenCalled();
103
+ consoleSpy.mockRestore();
104
+ });
105
+ });
@@ -0,0 +1,116 @@
1
+ import { createContext, useContext, useEffect, useState, type ReactNode } from "react";
2
+ import type { AuthMaterialWithUser, OAuthProvider } from "@crossmint/common-sdk-auth";
3
+ import type { UIConfig } from "@crossmint/common-sdk-base";
4
+ import type { LoginMethod } from "../CrossmintAuthProvider";
5
+
6
+ type AuthStep = "initial" | "walletMethod" | "otp" | "qrCode";
7
+ type OAuthUrlMap = Record<OAuthProvider, string>;
8
+ const initialOAuthUrlMap: OAuthUrlMap = {
9
+ google: "",
10
+ // Farcaster is not included here as it uses a different authentication method
11
+ };
12
+ interface AuthFormContextType {
13
+ step: AuthStep;
14
+ apiKey: string;
15
+ baseUrl: string;
16
+ fetchAuthMaterial: (refreshToken: string) => Promise<AuthMaterialWithUser>;
17
+ appearance?: UIConfig;
18
+ loginMethods: LoginMethod[];
19
+ oauthUrlMap: OAuthUrlMap;
20
+ isLoadingOauthUrlMap: boolean;
21
+ setStep: (step: AuthStep) => void;
22
+ setDialogOpen: (open: boolean) => void;
23
+ }
24
+
25
+ type ContextInitialStateProps = {
26
+ apiKey: string;
27
+ baseUrl: string;
28
+ fetchAuthMaterial: (refreshToken: string) => Promise<AuthMaterialWithUser>;
29
+ appearance?: UIConfig;
30
+ loginMethods: LoginMethod[];
31
+ setDialogOpen?: (open: boolean) => void;
32
+ };
33
+
34
+ const AuthFormContext = createContext<AuthFormContextType | undefined>(undefined);
35
+
36
+ export const useAuthForm = () => {
37
+ const context = useContext(AuthFormContext);
38
+ if (!context) {
39
+ throw new Error("useAuthForm must be used within an AuthFormProvider");
40
+ }
41
+ return context;
42
+ };
43
+
44
+ export const AuthFormProvider = ({
45
+ children,
46
+ initialState,
47
+ }: { children: ReactNode; initialState: ContextInitialStateProps }) => {
48
+ const [step, setStep] = useState<AuthStep>("initial");
49
+ const [oauthUrlMap, setOauthUrlMap] = useState<OAuthUrlMap>(initialOAuthUrlMap);
50
+ const [isLoadingOauthUrlMap, setIsLoadingOauthUrlMap] = useState(true);
51
+
52
+ const { loginMethods, apiKey, baseUrl } = initialState;
53
+
54
+ useEffect(() => {
55
+ const preFetchAndSetOauthUrl = async () => {
56
+ setIsLoadingOauthUrlMap(true);
57
+ try {
58
+ const oauthProviders = loginMethods.filter(
59
+ (method): method is OAuthProvider => method in initialOAuthUrlMap
60
+ );
61
+
62
+ const oauthPromiseList = oauthProviders.map(async (provider) => {
63
+ const url = await getOAuthUrl(provider, { apiKey, baseUrl });
64
+ return { [provider]: url };
65
+ });
66
+
67
+ const oauthUrlMap = Object.assign({}, ...(await Promise.all(oauthPromiseList)));
68
+ setOauthUrlMap(oauthUrlMap);
69
+ } catch (error) {
70
+ console.error("Error fetching OAuth URLs:", error);
71
+ } finally {
72
+ setIsLoadingOauthUrlMap(false);
73
+ }
74
+ };
75
+ preFetchAndSetOauthUrl();
76
+ }, [loginMethods, apiKey, baseUrl]);
77
+
78
+ const value: AuthFormContextType = {
79
+ step,
80
+ apiKey,
81
+ baseUrl,
82
+ fetchAuthMaterial: initialState.fetchAuthMaterial,
83
+ appearance: initialState.appearance,
84
+ loginMethods,
85
+ oauthUrlMap,
86
+ isLoadingOauthUrlMap,
87
+ setDialogOpen: initialState.setDialogOpen ?? (() => {}),
88
+ setStep,
89
+ };
90
+
91
+ return <AuthFormContext.Provider value={value}>{children}</AuthFormContext.Provider>;
92
+ };
93
+
94
+ async function getOAuthUrl(provider: OAuthProvider, options: { baseUrl: string; apiKey: string }) {
95
+ try {
96
+ const queryParams = new URLSearchParams({ apiKey: options.apiKey });
97
+ const response = await fetch(
98
+ `${options.baseUrl}api/2024-09-26/session/sdk/auth/social/${provider}/start?${queryParams}`,
99
+ {
100
+ headers: {
101
+ "x-api-key": options.apiKey,
102
+ },
103
+ }
104
+ );
105
+
106
+ if (!response.ok) {
107
+ throw new Error("Failed to get OAuth URL. Please try again or contact support.");
108
+ }
109
+
110
+ const data = (await response.json()) as { oauthUrl: string };
111
+ return data.oauthUrl;
112
+ } catch (error) {
113
+ console.error(`Error fetching OAuth URL for ${provider}:`, error);
114
+ throw new Error(`Failed to get OAuth URL for ${provider}. Please try again or contact support.`);
115
+ }
116
+ }
@@ -0,0 +1,12 @@
1
+ import type { ReactNode } from "react";
2
+ import { AuthKitProvider } from "@farcaster/auth-kit";
3
+
4
+ export function FarcasterProvider({ baseUrl, children }: { baseUrl: string; children: ReactNode }) {
5
+ const config = {
6
+ rpcUrl: "https://mainnet.optimism.io",
7
+ domain: new URL(baseUrl).hostname.replace(/^www\./, ""),
8
+ siweUri: `${baseUrl}api/2024-09-26/session/sdk/auth/authenticate`,
9
+ };
10
+
11
+ return <AuthKitProvider config={config}>{children}</AuthKitProvider>;
12
+ }
@@ -2,6 +2,106 @@ import { defineConfig } from "@twind/core";
2
2
  import presetTailwind from "@twind/preset-tailwind";
3
3
 
4
4
  export default defineConfig({
5
- presets: [presetTailwind(/* options */)],
5
+ presets: [presetTailwind()],
6
+ theme: {
7
+ screens: {
8
+ xs: "480px",
9
+ },
10
+ extend: {
11
+ colors: {
12
+ // Crossmint colors (cm- prefix)
13
+ "cm-text-primary": "#00150D",
14
+ "cm-text-secondary": "#67797F",
15
+ "cm-background-primary": "#FFFFFF",
16
+ "cm-muted-primary": "#F0F2F4",
17
+ "cm-border": "#D9D9D9",
18
+ "cm-ring": "#1A73E8",
19
+ },
20
+ keyframes: {
21
+ "caret-blink": {
22
+ "0%,70%,100%": { opacity: "1" },
23
+ "20%,50%": { opacity: "0" },
24
+ },
25
+ "fade-in": {
26
+ from: { opacity: "0" },
27
+ to: { opacity: "1" },
28
+ },
29
+ "fade-out": {
30
+ from: { opacity: "1" },
31
+ to: { opacity: "0" },
32
+ },
33
+ "slide-in-from-top": {
34
+ "0%": { transform: "translateY(-100%)" },
35
+ "100%": { transform: "translateY(0)" },
36
+ },
37
+ "slide-out-to-top": {
38
+ "0%": { transform: "translateY(0)" },
39
+ "100%": { transform: "translateY(-100%)" },
40
+ },
41
+ "slide-in-from-bottom": {
42
+ "0%": { transform: "translateY(100%)" },
43
+ "100%": { transform: "translateY(0)" },
44
+ },
45
+ "slide-out-to-bottom": {
46
+ "0%": { transform: "translateY(0)" },
47
+ "100%": { transform: "translateY(100%)" },
48
+ },
49
+ "slide-in-from-left": {
50
+ "0%": { transform: "translateX(-100%)" },
51
+ "100%": { transform: "translateX(0)" },
52
+ },
53
+ "slide-out-to-left": {
54
+ "0%": { transform: "translateX(0)" },
55
+ "100%": { transform: "translateX(-100%)" },
56
+ },
57
+ "slide-in-from-right": {
58
+ "0%": { transform: "translateX(100%)" },
59
+ "100%": { transform: "translateX(0)" },
60
+ },
61
+ "slide-out-to-right": {
62
+ "0%": { transform: "translateX(0)" },
63
+ "100%": { transform: "translateX(100%)" },
64
+ },
65
+ "zoom-in-95": {
66
+ "0%": { opacity: "0", transform: "scale(0.95) translate(-50%, -50%)" },
67
+ "100%": { opacity: "1", transform: "scale(1) translate(-50%, -50%)" },
68
+ },
69
+ "zoom-out-95": {
70
+ "0%": { opacity: "1", transform: "scale(1) translate(-50%, -50%)" },
71
+ "100%": { opacity: "0", transform: "scale(0.95) translate(-50%, -50%)" },
72
+ },
73
+ },
74
+ animation: {
75
+ "caret-blink": "caret-blink 1.25s ease-out infinite",
76
+ "fade-in": "fade-in 150ms cubic-bezier(0.16, 1, 0.3, 1)",
77
+ "fade-out": "fade-out 150ms cubic-bezier(0.16, 1, 0.3, 1)",
78
+ "slide-in-from-top": "slide-in-from-top 150ms cubic-bezier(0.16, 1, 0.3, 1)",
79
+ "slide-out-to-top": "slide-out-to-top 150ms cubic-bezier(0.16, 1, 0.3, 1)",
80
+ "slide-in-from-bottom": "slide-in-from-bottom 150ms cubic-bezier(0.16, 1, 0.3, 1)",
81
+ "slide-out-to-bottom": "slide-out-to-bottom 150ms cubic-bezier(0.16, 1, 0.3, 1)",
82
+ "slide-in-from-left": "slide-in-from-left 150ms cubic-bezier(0.16, 1, 0.3, 1)",
83
+ "slide-out-to-left": "slide-out-to-left 150ms cubic-bezier(0.16, 1, 0.3, 1)",
84
+ "slide-in-from-right": "slide-in-from-right 150ms cubic-bezier(0.16, 1, 0.3, 1)",
85
+ "slide-out-to-right": "slide-out-to-right 150ms cubic-bezier(0.16, 1, 0.3, 1)",
86
+ "zoom-in-95": "zoom-in-95 150ms cubic-bezier(0.16, 1, 0.3, 1)",
87
+ "zoom-out-95": "zoom-out-95 150ms cubic-bezier(0.16, 1, 0.3, 1)",
88
+ },
89
+ durations: {
90
+ "300": "300ms",
91
+ "500": "500ms",
92
+ },
93
+ },
94
+ },
95
+ rules: [
96
+ [
97
+ "responsive-border-radius-auth-dialog",
98
+ {
99
+ "@media (max-width: 479px)": {
100
+ "border-bottom-left-radius": "0 !important",
101
+ "border-bottom-right-radius": "0 !important",
102
+ },
103
+ },
104
+ ],
105
+ ],
6
106
  /* config */
7
107
  });
@@ -0,0 +1,4 @@
1
+ export type OtpEmailPayload = {
2
+ email: string;
3
+ emailId: string;
4
+ };
@@ -1,6 +1,3 @@
1
- export const SESSION_PREFIX = "crossmint-jwt";
2
- export const REFRESH_TOKEN_PREFIX = "crossmint-refresh-token";
3
-
4
1
  export function getCookie(name: string): string | undefined {
5
2
  const crossmintRefreshToken = document.cookie.split("; ").find((row) => row.startsWith(name));
6
3
  return crossmintRefreshToken ? crossmintRefreshToken.split("=")[1] : undefined;
@@ -0,0 +1,17 @@
1
+ import { LIB_VERSION } from "@/consts/version";
2
+ import { type Crossmint, CrossmintApiClient, type CrossmintApiClientInternalConfig } from "@crossmint/common-sdk-base";
3
+
4
+ export function createCrossmintApiClient(
5
+ crossmint: Crossmint,
6
+ apiKeyExpectations?: CrossmintApiClientInternalConfig["apiKeyExpectations"]
7
+ ) {
8
+ return new CrossmintApiClient(crossmint, {
9
+ internalConfig: {
10
+ sdkMetadata: {
11
+ name: "@crossmint/client-sdk-react-ui",
12
+ version: LIB_VERSION,
13
+ },
14
+ apiKeyExpectations,
15
+ },
16
+ });
17
+ }