@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.
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +20 -7
- package/dist/index.d.ts +20 -7
- package/dist/index.js +1 -1
- package/package.json +17 -7
- package/src/components/auth/AuthForm.tsx +50 -0
- package/src/components/auth/AuthFormBackButton.tsx +26 -0
- package/src/components/auth/AuthFormDialog.tsx +33 -0
- package/src/components/auth/EmbeddedAuthForm.tsx +5 -0
- package/src/components/auth/methods/email/EmailAuthFlow.tsx +19 -0
- package/src/components/auth/methods/email/EmailOTPInput.tsx +123 -0
- package/src/components/auth/methods/email/EmailSignIn.tsx +113 -0
- package/src/components/auth/methods/farcaster/FarcasterSignIn.tsx +170 -0
- package/src/components/auth/methods/google/GoogleSignIn.tsx +62 -0
- package/src/components/common/Dialog.tsx +141 -0
- package/src/components/common/Divider.tsx +25 -0
- package/src/components/common/InputOTP.tsx +89 -0
- package/src/components/common/PoweredByCrossmint.tsx +4 -9
- package/src/components/common/Spinner.tsx +22 -0
- package/src/components/dynamic-xyz/DynamicContextProviderWrapper.tsx +31 -0
- package/src/components/embed/v3/CrossmintEmbeddedCheckoutV3.tsx +7 -0
- package/src/components/embed/v3/EmbeddedCheckoutV3IFrame.tsx +74 -0
- package/src/components/embed/v3/crypto/CryptoWalletConnectionHandler.tsx +138 -0
- package/src/components/embed/v3/crypto/utils/handleEvmTransaction.ts +65 -0
- package/src/components/embed/v3/crypto/utils/handleSendTransaction.ts +31 -0
- package/src/components/embed/v3/crypto/utils/handleSolanaTransaction.ts +51 -0
- package/src/components/embed/v3/index.ts +1 -0
- package/src/components/index.ts +3 -0
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useAuthSignIn.ts +117 -0
- package/src/hooks/useCrossmintCheckout.tsx +54 -0
- package/src/hooks/useOAuthWindowListener.ts +87 -0
- package/src/hooks/useRefreshToken.test.ts +21 -8
- package/src/hooks/useRefreshToken.ts +5 -4
- package/src/icons/alert.tsx +19 -0
- package/src/icons/discord.tsx +18 -0
- package/src/icons/emailOTP.tsx +147 -0
- package/src/icons/farcaster.tsx +26 -0
- package/src/icons/google.tsx +30 -0
- package/src/icons/leftArrow.tsx +20 -0
- package/src/icons/poweredByLeaf.tsx +2 -2
- package/src/providers/CrossmintAuthProvider.test.tsx +4 -3
- package/src/providers/CrossmintAuthProvider.tsx +36 -32
- package/src/providers/CrossmintWalletProvider.tsx +3 -3
- package/src/providers/auth/AuthFormProvider.test.tsx +105 -0
- package/src/providers/auth/AuthFormProvider.tsx +116 -0
- package/src/providers/auth/FarcasterProvider.tsx +12 -0
- package/src/twind.config.ts +101 -1
- package/src/types/auth.ts +4 -0
- package/src/utils/authCookies.ts +0 -3
- package/src/utils/createCrossmintApiClient.ts +17 -0
- 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
|
-
|
|
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
|
|
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
|
-
|
|
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 [
|
|
72
|
+
const [dialogOpen, setDialogOpen] = useState(false);
|
|
68
73
|
|
|
69
|
-
const setAuthMaterial = (authMaterial:
|
|
70
|
-
setCookie(SESSION_PREFIX, authMaterial.
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
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<
|
|
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
|
-
|
|
156
|
+
showPasskeyHelpers={embeddedWallets.showPasskeyHelpers}
|
|
152
157
|
appearance={appearance}
|
|
153
158
|
>
|
|
154
|
-
<
|
|
155
|
-
{
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
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
|
-
|
|
60
|
+
showPasskeyHelpers = true, // enabled by default
|
|
61
61
|
appearance,
|
|
62
62
|
}: {
|
|
63
63
|
children: ReactNode;
|
|
64
64
|
defaultChain: EVMSmartWalletChain;
|
|
65
|
-
|
|
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 (
|
|
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
|
+
}
|
package/src/twind.config.ts
CHANGED
|
@@ -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(
|
|
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
|
});
|
package/src/utils/authCookies.ts
CHANGED
|
@@ -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
|
+
}
|