zh-web-sdk 2.16.1 → 2.17.0
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/README.md +225 -32
- package/package.json +6 -1
- package/.eslintrc.js +0 -12
- package/.github/CHANGELOG_TEMPLATE.md +0 -73
- package/.github/PULL_REQUEST_TEMPLATE.md +0 -10
- package/.github/backup/publish-tag.yaml +0 -14
- package/.github/workflows/build.yaml +0 -13
- package/.github/workflows/pr.yaml +0 -14
- package/.github/workflows/publish.yaml +0 -13
- package/.github/workflows/security.yml +0 -15
- package/.github/workflows/tag.yaml +0 -14
- package/RELEASING.md +0 -39
- package/jest.config.js +0 -8
- package/jest.setup.js +0 -24
- package/scripts/build.js +0 -34
- package/scripts/zip.js +0 -49
- package/src/__tests__/jwt-auth-detection.test.ts +0 -96
- package/src/api/convert-token.ts +0 -23
- package/src/constants.ts +0 -2
- package/src/hooks/__tests__/use-window-size.test.tsx +0 -26
- package/src/hooks/use-window-size.ts +0 -19
- package/src/iframe-container/AppContainer.tsx +0 -495
- package/src/iframe-container/__tests__/AppContainer.test.tsx +0 -300
- package/src/iframe-container/hooks/__tests__/use-style-updates.test.ts +0 -430
- package/src/iframe-container/hooks/use-style-updates.ts +0 -82
- package/src/index.tsx +0 -645
- package/src/redux/actions/index.ts +0 -27
- package/src/redux/reducers/constants.ts +0 -10
- package/src/redux/reducers/crypto-account-link-payouts.ts +0 -60
- package/src/redux/reducers/crypto-account-link.ts +0 -60
- package/src/redux/reducers/crypto-buy.ts +0 -75
- package/src/redux/reducers/crypto-sell.ts +0 -64
- package/src/redux/reducers/crypto-withdrawals.ts +0 -64
- package/src/redux/reducers/fiat-account-link.ts +0 -60
- package/src/redux/reducers/fiat-deposits.ts +0 -64
- package/src/redux/reducers/fiat-withdrawals.ts +0 -64
- package/src/redux/reducers/fund.ts +0 -75
- package/src/redux/reducers/index.ts +0 -35
- package/src/redux/reducers/onboarding.ts +0 -74
- package/src/redux/reducers/pay.ts +0 -64
- package/src/redux/reducers/payouts.ts +0 -64
- package/src/redux/reducers/profile.ts +0 -63
- package/src/redux/store/index.ts +0 -10
- package/src/styles.ts +0 -108
- package/src/types.ts +0 -578
- package/src/utils/auth-to-fund-mapper.ts +0 -174
- package/src/utils/strings.ts +0 -19
- package/src/utils/test-utils.tsx +0 -36
- package/src/utils/world-app-utils.ts +0 -8
- package/src/utils.ts +0 -27
- package/tsconfig.json +0 -26
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
import { decodeJWT } from "../api/convert-token";
|
|
2
|
-
|
|
3
|
-
describe("JWT Auth Detection", () => {
|
|
4
|
-
describe("decodeJWT", () => {
|
|
5
|
-
it("should decode a valid JWT with Connect issuer", () => {
|
|
6
|
-
// JWT with iss: "https://connect-xyz.us.auth0.com/"
|
|
7
|
-
const jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwczovL2Nvbm5lY3QueHl6L29yZ2FuaXphdGlvbiI6eyJpZCI6ImY0NDZmMTRjLTQwMzUtNDEwNS04OTJiLTE2NDliZmYxNjQyNCJ9LCJodHRwczovL2Nvbm5lY3QueHl6L2FjY291bnQiOnsiaWQiOiI1MzVhNzEyYS0wZmE0LTRjNmMtYjc4Zi01M2NiYTM1NmRkY2QiLCJyZWZlcmVuY2VJZCI6InJlZl8xNzY4MjYzNjg1NTM2X3ExM2tmdXc2ciJ9LCJodHRwczovL2Nvbm5lY3QueHl6L3VzZXIiOnsiaWQiOiJjZWU5ZmMzZS1lNjRmLTQ1OTItODQzZS1kN2ZhNzY2OWVmNGYifSwiaHR0cHM6Ly9jb25uZWN0Lnh5ei9zZXNzaW9uIjp7ImNyZWF0ZWRBdCI6IjIwMjYtMDEtMTNUMDA6MjE6MjZaIiwiaWQiOiI2MGQzZjkyYS1kNDlhLTQzM2EtOGU2OC1iMmZiNjE5ZDlmNTAiLCJtZXRhZGF0YSI6eyJ0ZXN0IjoidGVzdCJ9LCJzY29wZXMiOlsidXNlcjpkZXBvc2l0OnNlbmQiXSwic2V0dGluZ3MiOnsiemVyb19oYXNoIjp7ImVuYWJsZWQiOmZhbHNlfX19LCJpc3MiOiJodHRwczovL2Nvbm5lY3QteHl6LnVzLmF1dGgwLmNvbS8iLCJzdWIiOiJuTHBXSjU3U0Foa2JIODFST1ZKT0p3WVVYeDE5cjdYTkBjbGllbnRzIiwiYXVkIjoiaHR0cHM6Ly91c2Vycy5jZXJ0LmNvbm5lY3QueHl6IiwiaWF0IjoxNzY4MjYzNjg2LCJleHAiOjE3NjgzNTAwODYsImd0eSI6ImNsaWVudC1jcmVkZW50aWFscyIsImF6cCI6Im5McFdKNTdTQWhrYkg4MVJPVkpPSndZVVh4MTlyN1hOIn0.test";
|
|
8
|
-
|
|
9
|
-
const payload = decodeJWT(jwt);
|
|
10
|
-
|
|
11
|
-
expect(payload).toBeDefined();
|
|
12
|
-
expect(payload?.iss).toBe("https://connect-xyz.us.auth0.com/");
|
|
13
|
-
expect(payload?.iss).toContain("connect");
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
it("should decode a valid JWT without Connect issuer", () => {
|
|
17
|
-
// JWT with iss: "https://zerohash.com/"
|
|
18
|
-
const jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL3plcm9oYXNoLmNvbS8iLCJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.test";
|
|
19
|
-
|
|
20
|
-
const payload = decodeJWT(jwt);
|
|
21
|
-
|
|
22
|
-
expect(payload).toBeDefined();
|
|
23
|
-
expect(payload?.iss).toBe("https://zerohash.com/");
|
|
24
|
-
expect(payload?.iss).not.toContain("connect");
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it("should return null for invalid JWT format", () => {
|
|
28
|
-
const invalidJwt = "invalid.jwt";
|
|
29
|
-
|
|
30
|
-
const payload = decodeJWT(invalidJwt);
|
|
31
|
-
|
|
32
|
-
expect(payload).toBeNull();
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it("should return null for malformed JWT", () => {
|
|
36
|
-
const malformedJwt = "header.payload.signature.extra";
|
|
37
|
-
|
|
38
|
-
const payload = decodeJWT(malformedJwt);
|
|
39
|
-
|
|
40
|
-
expect(payload).toBeNull();
|
|
41
|
-
});
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
describe("useAuth determination logic", () => {
|
|
45
|
-
it("should determine useAuth=true when JWT has Connect issuer", () => {
|
|
46
|
-
const jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2Nvbm5lY3QteHl6LnVzLmF1dGgwLmNvbS8ifQ.test";
|
|
47
|
-
const payload = decodeJWT(jwt);
|
|
48
|
-
|
|
49
|
-
const useAuth = payload && payload.iss && typeof payload.iss === 'string' && payload.iss.includes('connect');
|
|
50
|
-
|
|
51
|
-
expect(useAuth).toBe(true);
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
it("should determine useAuth=false when JWT does not have Connect issuer", () => {
|
|
55
|
-
const jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL3plcm9oYXNoLmNvbS8ifQ.test";
|
|
56
|
-
const payload = decodeJWT(jwt);
|
|
57
|
-
|
|
58
|
-
const useAuth = payload && payload.iss && typeof payload.iss === 'string' && payload.iss.includes('connect');
|
|
59
|
-
|
|
60
|
-
expect(useAuth).toBe(false);
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it("should determine useAuth=false when JWT has no issuer", () => {
|
|
64
|
-
const jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.test";
|
|
65
|
-
const payload = decodeJWT(jwt);
|
|
66
|
-
|
|
67
|
-
const useAuth = payload && payload.iss && typeof payload.iss === 'string' && payload.iss.includes('connect');
|
|
68
|
-
|
|
69
|
-
expect(useAuth).toBeFalsy();
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
it("should determine useAuth=false when JWT is invalid", () => {
|
|
73
|
-
const jwt = "invalid";
|
|
74
|
-
const payload = decodeJWT(jwt);
|
|
75
|
-
|
|
76
|
-
const useAuth = payload && payload.iss && typeof payload.iss === 'string' && payload.iss.includes('connect');
|
|
77
|
-
|
|
78
|
-
expect(useAuth).toBeFalsy();
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
it("should match Connect issuer variations", () => {
|
|
82
|
-
const testCases = [
|
|
83
|
-
{ jwt: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2Nvbm5lY3QteHl6LnVzLmF1dGgwLmNvbS8ifQ.test", expected: true },
|
|
84
|
-
{ jwt: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2Nvbm5lY3QteHl6LmV1LmF1dGgwLmNvbS8ifQ.test", expected: true },
|
|
85
|
-
{ jwt: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2Nvbm5lY3QueHl6L2FwaS8ifQ.test", expected: true },
|
|
86
|
-
{ jwt: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL3plcm9oYXNoLmNvbS8ifQ.test", expected: false },
|
|
87
|
-
];
|
|
88
|
-
|
|
89
|
-
testCases.forEach(({ jwt, expected }) => {
|
|
90
|
-
const payload = decodeJWT(jwt);
|
|
91
|
-
const useAuth = payload && payload.iss && typeof payload.iss === 'string' && payload.iss.includes('connect');
|
|
92
|
-
expect(useAuth).toBe(expected);
|
|
93
|
-
});
|
|
94
|
-
});
|
|
95
|
-
});
|
|
96
|
-
});
|
package/src/api/convert-token.ts
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
export interface JWTPayload {
|
|
2
|
-
auth_embedded?: boolean;
|
|
3
|
-
reference_id?: string;
|
|
4
|
-
[key: string]: unknown;
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
export function decodeJWT(token: string): JWTPayload | null {
|
|
8
|
-
try {
|
|
9
|
-
const parts = token.split('.');
|
|
10
|
-
if (parts.length !== 3) {
|
|
11
|
-
console.error('[zh-web-sdk] Invalid JWT format');
|
|
12
|
-
return null;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const payload = parts[1];
|
|
16
|
-
const decoded = atob(payload.replace(/-/g, '+').replace(/_/g, '/'));
|
|
17
|
-
return JSON.parse(decoded);
|
|
18
|
-
} catch (error) {
|
|
19
|
-
console.error('[zh-web-sdk] Error decoding JWT:', error);
|
|
20
|
-
return null;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
package/src/constants.ts
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import {useWindowSize} from "../use-window-size";
|
|
2
|
-
import {act, renderHook, waitFor} from "@testing-library/react";
|
|
3
|
-
|
|
4
|
-
describe("useWindowSize", () => {
|
|
5
|
-
it("Returns correct window dimensions on redirect", async () => {
|
|
6
|
-
const { result } = renderHook(() => useWindowSize());
|
|
7
|
-
|
|
8
|
-
// Asset initial values.
|
|
9
|
-
expect(result.current.height).toBe(768);
|
|
10
|
-
expect(result.current.width).toBe(1024);
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
// Change the viewport to 500px.
|
|
14
|
-
global.innerWidth = 500;
|
|
15
|
-
global.innerHeight = 500;
|
|
16
|
-
|
|
17
|
-
act(() => {
|
|
18
|
-
global.dispatchEvent(new Event('resize'));
|
|
19
|
-
})
|
|
20
|
-
|
|
21
|
-
await waitFor(() => {
|
|
22
|
-
expect(result.current.width).toBe(500);
|
|
23
|
-
expect(result.current.height).toBe(500);
|
|
24
|
-
})
|
|
25
|
-
})
|
|
26
|
-
})
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import {useLayoutEffect, useState} from "react";
|
|
2
|
-
|
|
3
|
-
export const useWindowSize = () => {
|
|
4
|
-
const [height, setHeight] = useState(0);
|
|
5
|
-
const [width, setWidth] = useState(0);
|
|
6
|
-
useLayoutEffect(() => {
|
|
7
|
-
function updateSize() {
|
|
8
|
-
setWidth(window.innerWidth);
|
|
9
|
-
setHeight(window.innerHeight);
|
|
10
|
-
}
|
|
11
|
-
window.addEventListener('resize', updateSize);
|
|
12
|
-
updateSize();
|
|
13
|
-
return () => window.removeEventListener('resize', updateSize);
|
|
14
|
-
}, []);
|
|
15
|
-
return {
|
|
16
|
-
height,
|
|
17
|
-
width
|
|
18
|
-
};
|
|
19
|
-
}
|
|
@@ -1,495 +0,0 @@
|
|
|
1
|
-
import React, { useCallback, useEffect, useRef, useState } from "react";
|
|
2
|
-
import { connect, useSelector } from "react-redux";
|
|
3
|
-
import { Auth } from "@connect-xyz/auth-react";
|
|
4
|
-
import { createAuthToFundCallbacks } from "../utils/auth-to-fund-mapper";
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
containerMediaStyles,
|
|
8
|
-
containerStyle,
|
|
9
|
-
minWidthMatcher,
|
|
10
|
-
screenSizes,
|
|
11
|
-
appWrapperStyle as defaultAppWrapperStyle,
|
|
12
|
-
modalStyle as defaultModalStyle,
|
|
13
|
-
iframeWrapperStyle as defaultIframeWrapperStyle,
|
|
14
|
-
iframeStyle as defaultIframeStyle,
|
|
15
|
-
} from "../styles";
|
|
16
|
-
import { closeModal } from "../redux/actions";
|
|
17
|
-
import {
|
|
18
|
-
AppIdentifier,
|
|
19
|
-
appIdentifierToActionPrefixMap,
|
|
20
|
-
AuthSettings,
|
|
21
|
-
Filters,
|
|
22
|
-
IncomingMessageType,
|
|
23
|
-
OnboardingPage,
|
|
24
|
-
Page,
|
|
25
|
-
} from "../types";
|
|
26
|
-
import { useWindowSize } from "../hooks/use-window-size";
|
|
27
|
-
import { isInsideWorldApp } from "../utils/world-app-utils";
|
|
28
|
-
import {
|
|
29
|
-
PayCommandInput,
|
|
30
|
-
Tokens,
|
|
31
|
-
tokenToDecimals,
|
|
32
|
-
} from "@worldcoin/minikit-js";
|
|
33
|
-
import { useStyleUpdates } from "./hooks/use-style-updates";
|
|
34
|
-
|
|
35
|
-
interface AppContainerProps {
|
|
36
|
-
isAppActive?: boolean;
|
|
37
|
-
isAppLoaded?: boolean;
|
|
38
|
-
jwt?: string;
|
|
39
|
-
zeroHashAppURL: string;
|
|
40
|
-
appIdentifier: AppIdentifier;
|
|
41
|
-
filters?: Filters;
|
|
42
|
-
navigate?: Page;
|
|
43
|
-
useAuth?: boolean;
|
|
44
|
-
authSettings?: AuthSettings;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
interface AppContainerMappedProps {
|
|
48
|
-
[AppIdentifier.CRYPTO_WITHDRAWALS]: {
|
|
49
|
-
isAppActive: boolean;
|
|
50
|
-
isAppLoaded: boolean;
|
|
51
|
-
jwt: string;
|
|
52
|
-
filters?: Filters;
|
|
53
|
-
};
|
|
54
|
-
[AppIdentifier.ONBOARDING]: {
|
|
55
|
-
isAppActive: boolean;
|
|
56
|
-
isAppLoaded: boolean;
|
|
57
|
-
jwt: string;
|
|
58
|
-
filters?: Filters;
|
|
59
|
-
navigate?: OnboardingPage;
|
|
60
|
-
};
|
|
61
|
-
[AppIdentifier.FIAT_DEPOSITS]: {
|
|
62
|
-
isAppActive: boolean;
|
|
63
|
-
isAppLoaded: boolean;
|
|
64
|
-
jwt: string;
|
|
65
|
-
filters?: Filters;
|
|
66
|
-
};
|
|
67
|
-
[AppIdentifier.FIAT_WITHDRAWALS]: {
|
|
68
|
-
isAppActive: boolean;
|
|
69
|
-
isAppLoaded: boolean;
|
|
70
|
-
jwt: string;
|
|
71
|
-
filters?: Filters;
|
|
72
|
-
};
|
|
73
|
-
[AppIdentifier.CRYPTO_BUY]: {
|
|
74
|
-
isAppActive: boolean;
|
|
75
|
-
isAppLoaded: boolean;
|
|
76
|
-
jwt: string;
|
|
77
|
-
filters?: Filters;
|
|
78
|
-
};
|
|
79
|
-
[AppIdentifier.CRYPTO_SELL]: {
|
|
80
|
-
isAppActive: boolean;
|
|
81
|
-
isAppLoaded: boolean;
|
|
82
|
-
jwt: string;
|
|
83
|
-
filters?: Filters;
|
|
84
|
-
};
|
|
85
|
-
[AppIdentifier.CSP_CRYPTO_WITHDRAWALS]: {
|
|
86
|
-
isAppActive: boolean;
|
|
87
|
-
isAppLoaded: boolean;
|
|
88
|
-
jwt: string;
|
|
89
|
-
filters?: Filters;
|
|
90
|
-
};
|
|
91
|
-
[AppIdentifier.CSP_FIAT_WITHDRAWALS]: {
|
|
92
|
-
isAppActive: boolean;
|
|
93
|
-
isAppLoaded: boolean;
|
|
94
|
-
jwt: string;
|
|
95
|
-
filters?: Filters;
|
|
96
|
-
};
|
|
97
|
-
[AppIdentifier.CSP_CRYPTO_SELL]: {
|
|
98
|
-
isAppActive: boolean;
|
|
99
|
-
isAppLoaded: boolean;
|
|
100
|
-
jwt: string;
|
|
101
|
-
filters?: Filters;
|
|
102
|
-
};
|
|
103
|
-
[AppIdentifier.FUND]: {
|
|
104
|
-
isAppActive: boolean;
|
|
105
|
-
isAppLoaded: boolean;
|
|
106
|
-
jwt: string;
|
|
107
|
-
filters?: Filters;
|
|
108
|
-
useAuth?: boolean;
|
|
109
|
-
authSettings?: AuthSettings;
|
|
110
|
-
};
|
|
111
|
-
[AppIdentifier.PROFILE]: {
|
|
112
|
-
isAppActive: boolean;
|
|
113
|
-
isAppLoaded: boolean;
|
|
114
|
-
jwt: string;
|
|
115
|
-
filters?: Filters;
|
|
116
|
-
};
|
|
117
|
-
[AppIdentifier.CRYPTO_ACCOUNT_LINK]: {
|
|
118
|
-
isAppActive: boolean;
|
|
119
|
-
isAppLoaded: boolean;
|
|
120
|
-
jwt: string;
|
|
121
|
-
filters?: Filters;
|
|
122
|
-
};
|
|
123
|
-
[AppIdentifier.CRYPTO_ACCOUNT_LINK_PAYOUTS]: {
|
|
124
|
-
isAppActive: boolean;
|
|
125
|
-
isAppLoaded: boolean;
|
|
126
|
-
jwt: string;
|
|
127
|
-
filters?: Filters;
|
|
128
|
-
};
|
|
129
|
-
[AppIdentifier.PAYOUTS]: {
|
|
130
|
-
isAppActive: boolean;
|
|
131
|
-
isAppLoaded: boolean;
|
|
132
|
-
jwt: string;
|
|
133
|
-
filters?: Filters;
|
|
134
|
-
};
|
|
135
|
-
[AppIdentifier.PAY]: {
|
|
136
|
-
isAppActive: boolean;
|
|
137
|
-
isAppLoaded: boolean;
|
|
138
|
-
jwt: string;
|
|
139
|
-
filters?: Filters;
|
|
140
|
-
};
|
|
141
|
-
[AppIdentifier.FIAT_ACCOUNT_LINK]: {
|
|
142
|
-
isAppActive: boolean;
|
|
143
|
-
isAppLoaded: boolean;
|
|
144
|
-
jwt: string;
|
|
145
|
-
filters?: Filters;
|
|
146
|
-
};
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
const appLoadTime = Date.now();
|
|
150
|
-
|
|
151
|
-
const mapAppToTitle = {
|
|
152
|
-
[AppIdentifier.ONBOARDING]: "Onboarding",
|
|
153
|
-
[AppIdentifier.CRYPTO_WITHDRAWALS]: "Crypto Withdrawals",
|
|
154
|
-
[AppIdentifier.FIAT_DEPOSITS]: "Fiat Deposits",
|
|
155
|
-
[AppIdentifier.FIAT_WITHDRAWALS]: "Fiat Withdrawals",
|
|
156
|
-
[AppIdentifier.CRYPTO_BUY]: "Crypto Buy",
|
|
157
|
-
[AppIdentifier.CRYPTO_SELL]: "Crypto Sell",
|
|
158
|
-
[AppIdentifier.CSP_CRYPTO_WITHDRAWALS]: "CBP Crypto Withdrawals",
|
|
159
|
-
[AppIdentifier.CSP_FIAT_WITHDRAWALS]: "CBP Fiat Withdrawals",
|
|
160
|
-
[AppIdentifier.CSP_CRYPTO_SELL]: "Crypto Sell",
|
|
161
|
-
[AppIdentifier.FUND]: "Fund",
|
|
162
|
-
[AppIdentifier.PROFILE]: "Profile",
|
|
163
|
-
[AppIdentifier.CRYPTO_ACCOUNT_LINK]: "Crypto Account Link",
|
|
164
|
-
[AppIdentifier.CRYPTO_ACCOUNT_LINK_PAYOUTS]: "Crypto Account Link Payouts",
|
|
165
|
-
[AppIdentifier.PAYOUTS]: "Payouts",
|
|
166
|
-
[AppIdentifier.PAY]: "Pay",
|
|
167
|
-
[AppIdentifier.FIAT_ACCOUNT_LINK]: "Fiat Account Link",
|
|
168
|
-
};
|
|
169
|
-
|
|
170
|
-
const AppContainer = ({
|
|
171
|
-
isAppActive,
|
|
172
|
-
isAppLoaded,
|
|
173
|
-
jwt,
|
|
174
|
-
zeroHashAppURL,
|
|
175
|
-
appIdentifier,
|
|
176
|
-
navigate,
|
|
177
|
-
useAuth,
|
|
178
|
-
authSettings,
|
|
179
|
-
}: AppContainerProps) => {
|
|
180
|
-
const title = mapAppToTitle[appIdentifier];
|
|
181
|
-
const hasJwt = !!jwt;
|
|
182
|
-
const [container, setContainerStyle] = useState(containerStyle);
|
|
183
|
-
const allAppsState = useSelector((state) => state as AppContainerMappedProps);
|
|
184
|
-
const { height } = useWindowSize();
|
|
185
|
-
|
|
186
|
-
// Styles received from iframe via postMessage
|
|
187
|
-
const { styles, stylesLoaded, handleStyleConfig } = useStyleUpdates(
|
|
188
|
-
zeroHashAppURL,
|
|
189
|
-
{
|
|
190
|
-
appWrapperStyle: defaultAppWrapperStyle,
|
|
191
|
-
modalStyle: defaultModalStyle,
|
|
192
|
-
iframeWrapperStyle: defaultIframeWrapperStyle,
|
|
193
|
-
iframeStyle: defaultIframeStyle,
|
|
194
|
-
},
|
|
195
|
-
);
|
|
196
|
-
|
|
197
|
-
// Determine environment from zeroHashAppURL
|
|
198
|
-
const authEnv =
|
|
199
|
-
zeroHashAppURL.includes("sandbox") || zeroHashAppURL.includes("cert")
|
|
200
|
-
? "sandbox"
|
|
201
|
-
: "production";
|
|
202
|
-
|
|
203
|
-
/**
|
|
204
|
-
* setContainer sets the CSS styles based on the current matching media queries
|
|
205
|
-
*/
|
|
206
|
-
const setContainer = () => {
|
|
207
|
-
const sizeId = minWidthMatcher();
|
|
208
|
-
const containerMediaStyle = containerMediaStyles[sizeId];
|
|
209
|
-
setContainerStyle({ ...containerStyle, ...containerMediaStyle });
|
|
210
|
-
};
|
|
211
|
-
|
|
212
|
-
const iRef: React.MutableRefObject<HTMLIFrameElement | null> =
|
|
213
|
-
useRef<HTMLIFrameElement | null>(null);
|
|
214
|
-
useEffect(() => {
|
|
215
|
-
// Send events to the iframe when Apps are ready
|
|
216
|
-
if (
|
|
217
|
-
zeroHashAppURL &&
|
|
218
|
-
isAppLoaded &&
|
|
219
|
-
hasJwt &&
|
|
220
|
-
iRef.current?.contentWindow
|
|
221
|
-
) {
|
|
222
|
-
// This loops all registered apps and send events listed below.
|
|
223
|
-
Object.keys(allAppsState).forEach((key: string) => {
|
|
224
|
-
if (!allAppsState[key as AppIdentifier].jwt) return;
|
|
225
|
-
|
|
226
|
-
// Send JWT token
|
|
227
|
-
iRef.current?.contentWindow?.postMessage(
|
|
228
|
-
{
|
|
229
|
-
type: `${appIdentifierToActionPrefixMap.get(
|
|
230
|
-
key as AppIdentifier,
|
|
231
|
-
)}SEND_JWT_TOKEN`,
|
|
232
|
-
jwt: allAppsState[key as AppIdentifier].jwt,
|
|
233
|
-
},
|
|
234
|
-
zeroHashAppURL,
|
|
235
|
-
);
|
|
236
|
-
|
|
237
|
-
// If the zh-web-sdk is being run inside the WorldApp, we must send the WorldApp object
|
|
238
|
-
// to the iframe.
|
|
239
|
-
if (isInsideWorldApp()) {
|
|
240
|
-
iRef.current?.contentWindow?.postMessage(
|
|
241
|
-
{ type: `WORLD_APP_OBJECT`, WorldApp: window.WorldApp },
|
|
242
|
-
zeroHashAppURL,
|
|
243
|
-
);
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
// Send filters
|
|
247
|
-
if (allAppsState[key as AppIdentifier].filters) {
|
|
248
|
-
iRef.current?.contentWindow?.postMessage(
|
|
249
|
-
{
|
|
250
|
-
type: `${appIdentifierToActionPrefixMap.get(
|
|
251
|
-
key as AppIdentifier,
|
|
252
|
-
)}SEND_FILTERS`,
|
|
253
|
-
filters: allAppsState[key as AppIdentifier].filters,
|
|
254
|
-
},
|
|
255
|
-
zeroHashAppURL,
|
|
256
|
-
);
|
|
257
|
-
}
|
|
258
|
-
// Send navigate to Onboarding App
|
|
259
|
-
if (appIdentifier === AppIdentifier.ONBOARDING && navigate) {
|
|
260
|
-
iRef.current?.contentWindow?.postMessage(
|
|
261
|
-
{
|
|
262
|
-
type: `${appIdentifierToActionPrefixMap.get(
|
|
263
|
-
AppIdentifier.ONBOARDING,
|
|
264
|
-
)}NAVIGATE`,
|
|
265
|
-
navigate,
|
|
266
|
-
},
|
|
267
|
-
zeroHashAppURL,
|
|
268
|
-
);
|
|
269
|
-
}
|
|
270
|
-
});
|
|
271
|
-
}
|
|
272
|
-
}, [
|
|
273
|
-
appIdentifier,
|
|
274
|
-
hasJwt,
|
|
275
|
-
isAppLoaded,
|
|
276
|
-
jwt,
|
|
277
|
-
zeroHashAppURL,
|
|
278
|
-
allAppsState,
|
|
279
|
-
navigate,
|
|
280
|
-
]);
|
|
281
|
-
|
|
282
|
-
useEffect(() => {
|
|
283
|
-
// set the styles when the screen size changes
|
|
284
|
-
setContainer();
|
|
285
|
-
if (window) {
|
|
286
|
-
screenSizes.forEach(({ size }) => {
|
|
287
|
-
window
|
|
288
|
-
.matchMedia(`(min-width: ${size}px)`)
|
|
289
|
-
.addEventListener("change", setContainer);
|
|
290
|
-
});
|
|
291
|
-
return () =>
|
|
292
|
-
screenSizes.forEach(({ size }) => {
|
|
293
|
-
window
|
|
294
|
-
.matchMedia(`(min-width: ${size}px)`)
|
|
295
|
-
.removeEventListener("change", setContainer);
|
|
296
|
-
});
|
|
297
|
-
}
|
|
298
|
-
}, []);
|
|
299
|
-
|
|
300
|
-
/**
|
|
301
|
-
* Handles MiniKit pay command processing
|
|
302
|
-
*/
|
|
303
|
-
const handleMiniKitPayCommand = useCallback(
|
|
304
|
-
async (event: MessageEvent) => {
|
|
305
|
-
const zhAppsURL = new URL(zeroHashAppURL);
|
|
306
|
-
iRef.current?.contentWindow?.postMessage(
|
|
307
|
-
{
|
|
308
|
-
type: "MINIKIT_PAY_COMMAND_RECEIVED",
|
|
309
|
-
payload: {
|
|
310
|
-
origin: event.origin,
|
|
311
|
-
zhAppsURLOrigin: zhAppsURL.origin,
|
|
312
|
-
data: event.data,
|
|
313
|
-
},
|
|
314
|
-
},
|
|
315
|
-
zeroHashAppURL,
|
|
316
|
-
);
|
|
317
|
-
|
|
318
|
-
if (!window.MiniKit) {
|
|
319
|
-
iRef.current?.contentWindow?.postMessage(
|
|
320
|
-
{
|
|
321
|
-
type: "MINIKIT_PAY_COMMAND_CANCELED",
|
|
322
|
-
payload: { reason: "window.Minikit not found" },
|
|
323
|
-
},
|
|
324
|
-
zeroHashAppURL,
|
|
325
|
-
);
|
|
326
|
-
return;
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
try {
|
|
330
|
-
if (event.data?.type === "MINIKIT_PAY_COMMAND") {
|
|
331
|
-
const payload = event.data.payload;
|
|
332
|
-
const minikitPayload: PayCommandInput = {
|
|
333
|
-
reference: payload.reference,
|
|
334
|
-
to: payload.to,
|
|
335
|
-
tokens: [
|
|
336
|
-
{
|
|
337
|
-
symbol: payload.token as Tokens,
|
|
338
|
-
token_amount: tokenToDecimals(
|
|
339
|
-
Number(payload.amount),
|
|
340
|
-
payload.token,
|
|
341
|
-
).toString(),
|
|
342
|
-
},
|
|
343
|
-
],
|
|
344
|
-
description: payload.description,
|
|
345
|
-
};
|
|
346
|
-
iRef.current?.contentWindow?.postMessage(
|
|
347
|
-
{
|
|
348
|
-
type: "MINIKIT_PAY_COMMAND_SUBMITTING_PAYMENT",
|
|
349
|
-
payload: { minikitPayload },
|
|
350
|
-
},
|
|
351
|
-
zeroHashAppURL,
|
|
352
|
-
);
|
|
353
|
-
const { finalPayload } =
|
|
354
|
-
await window.MiniKit.commandsAsync.pay(minikitPayload);
|
|
355
|
-
|
|
356
|
-
iRef.current?.contentWindow?.postMessage(
|
|
357
|
-
{ type: "MINIKIT_PAY_COMMAND_RESULT", payload: { finalPayload } },
|
|
358
|
-
zeroHashAppURL,
|
|
359
|
-
);
|
|
360
|
-
}
|
|
361
|
-
} catch (error) {
|
|
362
|
-
iRef.current?.contentWindow?.postMessage(
|
|
363
|
-
{
|
|
364
|
-
type: "MINIKIT_PAY_COMMAND_ERROR",
|
|
365
|
-
payload: { error, status: "error" },
|
|
366
|
-
},
|
|
367
|
-
zeroHashAppURL,
|
|
368
|
-
);
|
|
369
|
-
}
|
|
370
|
-
},
|
|
371
|
-
[zeroHashAppURL],
|
|
372
|
-
);
|
|
373
|
-
|
|
374
|
-
/**
|
|
375
|
-
* Main postMessage event handler that routes messages to appropriate handlers
|
|
376
|
-
*/
|
|
377
|
-
const postMessageEventHandler = useCallback(
|
|
378
|
-
async (event: MessageEvent) => {
|
|
379
|
-
// Handle style configuration messages
|
|
380
|
-
if (event.data.type === IncomingMessageType.StyleConfig) {
|
|
381
|
-
handleStyleConfig(event.data, event.origin);
|
|
382
|
-
return;
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
// Handle MiniKit pay commands
|
|
386
|
-
await handleMiniKitPayCommand(event);
|
|
387
|
-
},
|
|
388
|
-
[handleStyleConfig, handleMiniKitPayCommand],
|
|
389
|
-
);
|
|
390
|
-
|
|
391
|
-
useEffect(() => {
|
|
392
|
-
window.addEventListener("message", postMessageEventHandler);
|
|
393
|
-
return () => window.removeEventListener("message", postMessageEventHandler);
|
|
394
|
-
}, [postMessageEventHandler, zeroHashAppURL]);
|
|
395
|
-
|
|
396
|
-
const iframeURL = new URL(zeroHashAppURL);
|
|
397
|
-
iframeURL.searchParams.set("name", appLoadTime.toString());
|
|
398
|
-
if (window) {
|
|
399
|
-
iframeURL.searchParams.set("origin", window.location.origin);
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
if (!isAppActive) {
|
|
403
|
-
return <></>;
|
|
404
|
-
}
|
|
405
|
-
const { appWrapperStyle, modalStyle, iframeWrapperStyle, iframeStyle } =
|
|
406
|
-
styles;
|
|
407
|
-
|
|
408
|
-
// If useAuth is true, render Auth component instead of iframe
|
|
409
|
-
if (useAuth && hasJwt) {
|
|
410
|
-
const authCallbacks = createAuthToFundCallbacks(window.location.origin);
|
|
411
|
-
const theme = authSettings?.theme || "auto";
|
|
412
|
-
|
|
413
|
-
return (
|
|
414
|
-
<div
|
|
415
|
-
style={{
|
|
416
|
-
...appWrapperStyle,
|
|
417
|
-
maxHeight: CSS.supports("height: 100dvh") ? "100dvh" : `${height}px`,
|
|
418
|
-
height: CSS.supports("height: 100dvh") ? "100dvh" : `${height}px`,
|
|
419
|
-
}}
|
|
420
|
-
onClick={() => closeModal(appIdentifier)}
|
|
421
|
-
>
|
|
422
|
-
<div
|
|
423
|
-
style={{
|
|
424
|
-
...modalStyle,
|
|
425
|
-
...container,
|
|
426
|
-
backgroundColor: "transparent",
|
|
427
|
-
zIndex: "9999",
|
|
428
|
-
opacity: stylesLoaded ? 1 : 0,
|
|
429
|
-
}}
|
|
430
|
-
onClick={(e) => e.stopPropagation()}
|
|
431
|
-
>
|
|
432
|
-
<div style={{ ...iframeWrapperStyle, ...iframeStyle }}>
|
|
433
|
-
<Auth jwt={jwt} env={authEnv} theme={theme} {...authCallbacks} />
|
|
434
|
-
</div>
|
|
435
|
-
</div>
|
|
436
|
-
</div>
|
|
437
|
-
);
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
return (
|
|
441
|
-
<div
|
|
442
|
-
style={{
|
|
443
|
-
...appWrapperStyle,
|
|
444
|
-
/**
|
|
445
|
-
* If browser does not support 100dvh, use height in pixels that is recalculated when
|
|
446
|
-
* the window is resized.
|
|
447
|
-
*/
|
|
448
|
-
maxHeight: CSS.supports("height: 100dvh") ? "100dvh" : `${height}px`,
|
|
449
|
-
height: CSS.supports("height: 100dvh") ? "100dvh" : `${height}px`,
|
|
450
|
-
}}
|
|
451
|
-
onClick={() => closeModal(appIdentifier)}
|
|
452
|
-
>
|
|
453
|
-
<div
|
|
454
|
-
style={{ ...modalStyle, ...container, opacity: stylesLoaded ? 1 : 0 }}
|
|
455
|
-
>
|
|
456
|
-
<div style={iframeWrapperStyle}>
|
|
457
|
-
<iframe
|
|
458
|
-
ref={iRef}
|
|
459
|
-
title={title}
|
|
460
|
-
src={iframeURL.toString()}
|
|
461
|
-
allow="camera *; fullscreen *; accelerometer *; gyroscope *; magnetometer *; local-network-access *;"
|
|
462
|
-
sandbox="allow-downloads allow-forms allow-modals allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts allow-top-navigation"
|
|
463
|
-
style={iframeStyle}
|
|
464
|
-
/>
|
|
465
|
-
</div>
|
|
466
|
-
</div>
|
|
467
|
-
</div>
|
|
468
|
-
);
|
|
469
|
-
};
|
|
470
|
-
|
|
471
|
-
const mapStateToProps = (
|
|
472
|
-
state: AppContainerMappedProps,
|
|
473
|
-
ownProps: AppContainerProps,
|
|
474
|
-
) => {
|
|
475
|
-
return {
|
|
476
|
-
isAppActive: state[ownProps.appIdentifier].isAppActive,
|
|
477
|
-
isAppLoaded: state[ownProps.appIdentifier].isAppLoaded,
|
|
478
|
-
jwt: state[ownProps.appIdentifier].jwt,
|
|
479
|
-
filters: state[ownProps.appIdentifier].filters,
|
|
480
|
-
navigate:
|
|
481
|
-
ownProps.appIdentifier === AppIdentifier.ONBOARDING
|
|
482
|
-
? state[ownProps.appIdentifier].navigate
|
|
483
|
-
: undefined,
|
|
484
|
-
useAuth:
|
|
485
|
-
ownProps.appIdentifier === AppIdentifier.FUND
|
|
486
|
-
? state[ownProps.appIdentifier].useAuth
|
|
487
|
-
: undefined,
|
|
488
|
-
authSettings:
|
|
489
|
-
ownProps.appIdentifier === AppIdentifier.FUND
|
|
490
|
-
? state[ownProps.appIdentifier].authSettings
|
|
491
|
-
: undefined,
|
|
492
|
-
};
|
|
493
|
-
};
|
|
494
|
-
|
|
495
|
-
export default connect(mapStateToProps)(AppContainer);
|