@crossmint/client-sdk-react-ui 1.3.8 → 1.3.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/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +30 -19
- package/dist/index.d.ts +30 -19
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
- package/src/components/CrossmintCollectionView.test.tsx +24 -22
- package/src/components/CrossmintNFTDetail.test.tsx +24 -22
- package/src/components/embed/CrossmintPaymentElement.test.tsx +4 -3
- package/src/components/embed/EmbeddedCheckoutIFrame.tsx +6 -2
- package/src/components/embed/crypto/CryptoEmbeddedCheckoutIFrame.tsx +1 -1
- package/src/components/embed/fiat/FiatEmbeddedCheckout.tsx +2 -2
- package/src/components/embed/fiat/FiatEmbeddedCheckoutIFrame.tsx +1 -2
- package/src/components/hosted/CrossmintPayButton.test.tsx +21 -20
- package/src/consts/version.ts +1 -1
- package/src/hooks/index.ts +2 -1
- package/src/hooks/useCrossmint.test.tsx +17 -19
- package/src/hooks/useCrossmint.tsx +14 -10
- package/src/hooks/useWallet.ts +12 -0
- package/src/providers/CrossmintAuthProvider.tsx +15 -50
- package/src/providers/CrossmintWalletProvider.tsx +80 -0
- package/src/providers/index.ts +1 -0
- package/src/hooks/useAuth.ts +0 -15
|
@@ -1,23 +1,17 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { fireEvent, render } from "@testing-library/react";
|
|
2
2
|
import { useEffect } from "react";
|
|
3
|
+
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
3
4
|
|
|
4
|
-
import { Crossmint } from "@crossmint/common-sdk-base";
|
|
5
|
+
import { Crossmint, createCrossmint } from "@crossmint/common-sdk-base";
|
|
5
6
|
|
|
6
7
|
import { CrossmintProvider, useCrossmint } from "./useCrossmint";
|
|
7
8
|
|
|
8
9
|
const MOCK_API_KEY =
|
|
9
10
|
"sk_development_5ZUNkuhjP8aYZEgUTDfWToqFpo5zakEqte1db4pHZgPAVKZ9JuSvnKeGiqY654DoBuuZEzYz4Eb8gRV2ePqQ1fxTjEP8tTaUQdzbGfyG9RgyeN5YbqViXinqxk8EayEkAGtvSSgjpjEr6iaBptJtUFwPW59DjQzTQP6P8uZdiajenVg7bARGKjzFyByNuVEoz41DpRB4hDZNFdwCTuf5joFv";
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
return {
|
|
15
|
-
...actualModule,
|
|
16
|
-
createCrossmint: jest.fn(() => ({
|
|
17
|
-
apiKey: MOCK_API_KEY,
|
|
18
|
-
})),
|
|
19
|
-
};
|
|
20
|
-
});
|
|
12
|
+
vi.mock("@crossmint/common-sdk-base", () => ({
|
|
13
|
+
createCrossmint: vi.fn(),
|
|
14
|
+
}));
|
|
21
15
|
|
|
22
16
|
class MockSDK {
|
|
23
17
|
constructor(public crossmint: Crossmint) {}
|
|
@@ -31,6 +25,14 @@ function renderCrossmintProvider({ children }: { children: JSX.Element }) {
|
|
|
31
25
|
}
|
|
32
26
|
|
|
33
27
|
describe("CrossmintProvider", () => {
|
|
28
|
+
beforeEach(() => {
|
|
29
|
+
vi.resetAllMocks();
|
|
30
|
+
vi.mocked(createCrossmint).mockImplementation(() => ({
|
|
31
|
+
apiKey: MOCK_API_KEY,
|
|
32
|
+
jwt: "",
|
|
33
|
+
}));
|
|
34
|
+
});
|
|
35
|
+
|
|
34
36
|
it("provides initial JWT value", () => {
|
|
35
37
|
const TestComponent = () => {
|
|
36
38
|
const { crossmint } = useCrossmint();
|
|
@@ -51,9 +53,7 @@ describe("CrossmintProvider", () => {
|
|
|
51
53
|
);
|
|
52
54
|
};
|
|
53
55
|
const { getByTestId, getByText } = renderCrossmintProvider({ children: <TestComponent /> });
|
|
54
|
-
|
|
55
|
-
getByText("Update JWT").click();
|
|
56
|
-
});
|
|
56
|
+
fireEvent.click(getByText("Update JWT"));
|
|
57
57
|
expect(getByTestId("jwt").textContent).toBe("new_jwt");
|
|
58
58
|
});
|
|
59
59
|
|
|
@@ -71,7 +71,7 @@ describe("CrossmintProvider", () => {
|
|
|
71
71
|
});
|
|
72
72
|
|
|
73
73
|
it("triggers re-render on JWT change", () => {
|
|
74
|
-
const renderCount =
|
|
74
|
+
const renderCount = vi.fn();
|
|
75
75
|
const TestComponent = () => {
|
|
76
76
|
const { crossmint, setJwt } = useCrossmint();
|
|
77
77
|
useEffect(() => {
|
|
@@ -89,9 +89,7 @@ describe("CrossmintProvider", () => {
|
|
|
89
89
|
|
|
90
90
|
expect(renderCount).toHaveBeenCalledTimes(1);
|
|
91
91
|
|
|
92
|
-
|
|
93
|
-
getByText("Update JWT").click();
|
|
94
|
-
});
|
|
92
|
+
fireEvent.click(getByText("Update JWT"));
|
|
95
93
|
|
|
96
94
|
expect(renderCount).toHaveBeenCalledTimes(2);
|
|
97
95
|
});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ReactNode, createContext, useCallback, useContext, useMemo, useRef, useState } from "react";
|
|
2
2
|
|
|
3
|
+
import { getCachedJwt } from "@crossmint/client-sdk-auth-core/client";
|
|
3
4
|
import { Crossmint, createCrossmint } from "@crossmint/common-sdk-base";
|
|
4
5
|
|
|
5
6
|
export interface CrossmintContext {
|
|
@@ -16,14 +17,17 @@ export function CrossmintProvider({
|
|
|
16
17
|
const [version, setVersion] = useState(0);
|
|
17
18
|
|
|
18
19
|
const crossmintRef = useRef<Crossmint>(
|
|
19
|
-
new Proxy<Crossmint>(
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
20
|
+
new Proxy<Crossmint>(
|
|
21
|
+
createCrossmint({ ...createCrossmintParams, jwt: createCrossmintParams.jwt ?? getCachedJwt() }),
|
|
22
|
+
{
|
|
23
|
+
set(target, prop, value) {
|
|
24
|
+
if (prop === "jwt" && target.jwt !== value) {
|
|
25
|
+
setVersion((v) => v + 1);
|
|
26
|
+
}
|
|
27
|
+
return Reflect.set(target, prop, value);
|
|
28
|
+
},
|
|
29
|
+
}
|
|
30
|
+
)
|
|
27
31
|
);
|
|
28
32
|
|
|
29
33
|
const setJwt = useCallback((jwt: string | undefined) => {
|
|
@@ -45,10 +49,10 @@ export function CrossmintProvider({
|
|
|
45
49
|
return <CrossmintContext.Provider value={value}>{children}</CrossmintContext.Provider>;
|
|
46
50
|
}
|
|
47
51
|
|
|
48
|
-
export function useCrossmint() {
|
|
52
|
+
export function useCrossmint(missingContextMessage?: string) {
|
|
49
53
|
const context = useContext(CrossmintContext);
|
|
50
54
|
if (context == null) {
|
|
51
|
-
throw new Error("useCrossmint must be used within a CrossmintProvider");
|
|
55
|
+
throw new Error(missingContextMessage ?? "useCrossmint must be used within a CrossmintProvider");
|
|
52
56
|
}
|
|
53
57
|
return context;
|
|
54
58
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { WalletContext } from "@/providers/CrossmintWalletProvider";
|
|
2
|
+
import { useContext } from "react";
|
|
3
|
+
|
|
4
|
+
export function useWallet() {
|
|
5
|
+
const walletContext = useContext(WalletContext);
|
|
6
|
+
|
|
7
|
+
if (!walletContext) {
|
|
8
|
+
throw new Error("useWallet must be used within CrossmintAuthProvider or CrossmintWalletProvider");
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
return walletContext;
|
|
12
|
+
}
|
|
@@ -1,57 +1,22 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useCrossmint } from "@/hooks";
|
|
2
|
+
import { ReactNode } from "react";
|
|
2
3
|
|
|
3
|
-
import { AuthProvider
|
|
4
|
-
import { EVMSmartWallet, SmartWalletSDK } from "@crossmint/client-sdk-smart-wallet";
|
|
4
|
+
import { AuthProvider as AuthCoreProvider } from "@crossmint/client-sdk-auth-core/client";
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
embeddedWallets: {
|
|
8
|
-
type: "evm-smart-wallet";
|
|
9
|
-
defaultChain: "polygon-amoy" | "base-sepolia";
|
|
10
|
-
createOnLogin: "all-users" | "off";
|
|
11
|
-
};
|
|
12
|
-
};
|
|
6
|
+
import { CrossmintWalletConfig, CrossmintWalletProvider } from "./CrossmintWalletProvider";
|
|
13
7
|
|
|
14
|
-
export
|
|
15
|
-
|
|
16
|
-
|
|
8
|
+
export function CrossmintAuthProvider({
|
|
9
|
+
embeddedWallets,
|
|
10
|
+
children,
|
|
11
|
+
}: {
|
|
12
|
+
embeddedWallets: CrossmintWalletConfig;
|
|
13
|
+
children: ReactNode;
|
|
14
|
+
}) {
|
|
15
|
+
const { crossmint, setJwt } = useCrossmint("CrossmintAuthProvider must be used within CrossmintProvider");
|
|
17
16
|
|
|
18
|
-
export function CrossmintAuthProvider(props: AuthWalletProviderParams) {
|
|
19
17
|
return (
|
|
20
|
-
<
|
|
21
|
-
<
|
|
22
|
-
</
|
|
18
|
+
<AuthCoreProvider setJwtToken={setJwt} crossmint={crossmint}>
|
|
19
|
+
<CrossmintWalletProvider config={embeddedWallets}>{children}</CrossmintWalletProvider>
|
|
20
|
+
</AuthCoreProvider>
|
|
23
21
|
);
|
|
24
22
|
}
|
|
25
|
-
|
|
26
|
-
function AuthWalletProvider(props: AuthWalletProviderParams) {
|
|
27
|
-
const { jwt } = useAuthCore();
|
|
28
|
-
|
|
29
|
-
const [wallet, setWallet] = useState<EVMSmartWallet | null>(null);
|
|
30
|
-
|
|
31
|
-
const smartWalletSDK = useMemo(() => SmartWalletSDK.init({ clientApiKey: props.apiKey }), [props.apiKey]);
|
|
32
|
-
|
|
33
|
-
const getOrCreateWallet = async (jwt: string) => {
|
|
34
|
-
try {
|
|
35
|
-
const wallet = await smartWalletSDK.getOrCreateWallet({ jwt }, props.embeddedWallets.defaultChain);
|
|
36
|
-
setWallet(wallet);
|
|
37
|
-
} catch (e: any) {
|
|
38
|
-
console.error("There was an error creating a wallet ", e);
|
|
39
|
-
throw e;
|
|
40
|
-
}
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
useEffect(() => {
|
|
44
|
-
if (props.embeddedWallets.createOnLogin && jwt) {
|
|
45
|
-
console.log("Getting or Creating wallet");
|
|
46
|
-
getOrCreateWallet(jwt);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
if (wallet && !jwt) {
|
|
50
|
-
// implies a logout has occurred, clear wallet
|
|
51
|
-
console.log("Clearing wallet");
|
|
52
|
-
setWallet(null);
|
|
53
|
-
}
|
|
54
|
-
}, [jwt, props.embeddedWallets.createOnLogin]);
|
|
55
|
-
|
|
56
|
-
return <WalletContext.Provider value={{ wallet }}>{props.children}</WalletContext.Provider>;
|
|
57
|
-
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { useCrossmint } from "@/hooks";
|
|
2
|
+
import { ReactNode, createContext, useEffect, useMemo, useState } from "react";
|
|
3
|
+
|
|
4
|
+
import { EVMSmartWallet, SmartWalletError, SmartWalletSDK } from "@crossmint/client-sdk-smart-wallet";
|
|
5
|
+
|
|
6
|
+
export type CrossmintWalletConfig = {
|
|
7
|
+
type: "evm-smart-wallet";
|
|
8
|
+
defaultChain: "polygon-amoy" | "base-sepolia" | "optimism-sepolia" | "arbitrum-sepolia";
|
|
9
|
+
createOnLogin: "all-users" | "off";
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
type WalletStatus = "not-loaded" | "in-progress" | "loaded" | "loading-error";
|
|
13
|
+
|
|
14
|
+
type ValidWalletState =
|
|
15
|
+
| { status: "not-loaded" | "in-progress" }
|
|
16
|
+
| { status: "loaded"; wallet: EVMSmartWallet }
|
|
17
|
+
| { status: "loading-error"; error: SmartWalletError };
|
|
18
|
+
|
|
19
|
+
function deriveErrorState(error: unknown): { status: "loading-error"; error: SmartWalletError } {
|
|
20
|
+
if (error instanceof SmartWalletError) {
|
|
21
|
+
return { status: "loading-error", error };
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
25
|
+
const stack = error instanceof Error ? error.stack : undefined;
|
|
26
|
+
return { status: "loading-error", error: new SmartWalletError(`Unknown Wallet Error: ${message}`, stack) };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function shouldGetOrCreateWallet(status: WalletStatus, jwt: string | undefined): jwt is string {
|
|
30
|
+
return jwt != null && !(status === "in-progress" || status === "loaded");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
type WalletContext = {
|
|
34
|
+
status: WalletStatus;
|
|
35
|
+
wallet?: EVMSmartWallet;
|
|
36
|
+
error?: SmartWalletError;
|
|
37
|
+
getOrCreateWallet: () => Promise<void>;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export const WalletContext = createContext<WalletContext>({
|
|
41
|
+
status: "not-loaded",
|
|
42
|
+
getOrCreateWallet: async () => {},
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
export function CrossmintWalletProvider({ children, config }: { config: CrossmintWalletConfig; children: ReactNode }) {
|
|
46
|
+
const { crossmint } = useCrossmint("CrossmintWalletProvider must be used within CrossmintProvider");
|
|
47
|
+
const [state, setState] = useState<ValidWalletState>({ status: "not-loaded" });
|
|
48
|
+
const smartWalletSDK = useMemo(() => SmartWalletSDK.init({ clientApiKey: crossmint.apiKey }), [crossmint.apiKey]);
|
|
49
|
+
|
|
50
|
+
const getOrCreateWallet = async () => {
|
|
51
|
+
if (!shouldGetOrCreateWallet(state.status, crossmint.jwt)) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
try {
|
|
56
|
+
setState({ status: "in-progress" });
|
|
57
|
+
const wallet = await smartWalletSDK.getOrCreateWallet({ jwt: crossmint.jwt }, config.defaultChain);
|
|
58
|
+
setState({ status: "loaded", wallet });
|
|
59
|
+
} catch (error: unknown) {
|
|
60
|
+
console.error("There was an error creating a wallet ", error);
|
|
61
|
+
setState(deriveErrorState(error));
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
useEffect(() => {
|
|
66
|
+
if (config.createOnLogin === "all-users" && shouldGetOrCreateWallet(state.status, crossmint.jwt)) {
|
|
67
|
+
console.log("Getting or Creating wallet");
|
|
68
|
+
getOrCreateWallet();
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (state.status === "loaded" && crossmint.jwt == null) {
|
|
73
|
+
console.log("Clearing wallet");
|
|
74
|
+
setState({ status: "not-loaded" });
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
}, [crossmint.jwt, config.createOnLogin, state.status]);
|
|
78
|
+
|
|
79
|
+
return <WalletContext.Provider value={{ ...state, getOrCreateWallet }}>{children}</WalletContext.Provider>;
|
|
80
|
+
}
|
package/src/providers/index.ts
CHANGED
package/src/hooks/useAuth.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { WalletContext } from "@/providers/CrossmintAuthProvider";
|
|
2
|
-
import { useContext } from "react";
|
|
3
|
-
|
|
4
|
-
import { useAuth as useAuthCore } from "@crossmint/client-sdk-auth-core/client";
|
|
5
|
-
|
|
6
|
-
export function useAuth() {
|
|
7
|
-
const walletContext = useContext(WalletContext);
|
|
8
|
-
|
|
9
|
-
if (!walletContext) {
|
|
10
|
-
throw new Error("useAuth must be used within a CrossmintAuthProvider");
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
const authContext = useAuthCore();
|
|
14
|
-
return { ...authContext, ...walletContext };
|
|
15
|
-
}
|