diff --git a/example/src/index.tsx b/example/src/index.tsx index ea81f11..f0b0ccf 100644 --- a/example/src/index.tsx +++ b/example/src/index.tsx @@ -27,12 +27,10 @@ const appQueryClient = new QueryClient({ const passportEmbedParams = { apiKey: import.meta.env.VITE_API_KEY, scorerId: import.meta.env.VITE_SCORER_ID, - overrideIamUrl: "https://embed.review.passport.xyz", - challengeSignatureUrl: "https://iam.review.passport.xyz/api/v0.0.0/challenge", - // challengeSignatureUrl: "http://localhost:8003/api/v0.0.0/challenge", - // oAuthPopUpUrl: "http://localhost:3010", - oAuthPopUpUrl: - "http://passport-embed-popup-review.s3-website-us-west-2.amazonaws.com", + // overrideIamUrl: "https://embed.review.passport.xyz", + overrideIamUrl: "http://localhost:8004", + // challengeSignatureUrl: "https://iam.review.passport.xyz/api/v0.0.0/challenge", + challengeSignatureUrl: "http://localhost:8003/api/v0.0.0/challenge", }; const connectWallet = async () => { diff --git a/example/yarn.lock b/example/yarn.lock index 321a33a..cb9d7e3 100644 --- a/example/yarn.lock +++ b/example/yarn.lock @@ -206,6 +206,7 @@ dependencies: "@tanstack/react-query" "^5.62.11" axios "^1.7.7" + buffer "^6.0.3" "@rollup/rollup-darwin-arm64@4.24.0": version "4.24.0" diff --git a/package.json b/package.json index aa43042..50f3494 100644 --- a/package.json +++ b/package.json @@ -55,6 +55,7 @@ }, "dependencies": { "@tanstack/react-query": "^5.62.11", - "axios": "^1.7.7" + "axios": "^1.7.7", + "buffer": "^6.0.3" } -} \ No newline at end of file +} diff --git a/src/components/Body/PlatformVerification.tsx b/src/components/Body/PlatformVerification.tsx index f82199b..276eef7 100644 --- a/src/components/Body/PlatformVerification.tsx +++ b/src/components/Body/PlatformVerification.tsx @@ -3,7 +3,8 @@ import utilStyles from "../../utilStyles.module.css"; import { useEffect, useState } from "react"; import { Buffer } from "buffer"; import { Button } from "../Button"; -import { Hyperlink, Platform, usePlatformStatus } from "./ScoreTooLowBody"; +import { Hyperlink, usePlatformStatus } from "./ScoreTooLowBody"; +import { Platform } from "../../hooks/useStampPages"; import { ScrollableDiv } from "../ScrollableDiv"; import { useWidgetIsQuerying, @@ -14,8 +15,6 @@ import { useQueryContext } from "../../contexts/QueryContext"; const DEFAULT_CHALLENGE_URL = "https://iam.review.passport.xyz/api/v0.0.0/challenge"; -const DEFAULT_OAUTH_POPUP = "https://embed-popup.passport.xyz"; - const CloseIcon = () => ( ) : ( - platform.description +
)}
- ), - documentationLink: "https://google.com", - credentials: [ - { - // id: "BinanceBABT2", - id: "NFT", - weight: "16", - }, - ], - }, - { - name: "Holonym", - description:
TODO
, - documentationLink: "https://google.com", - credentials: [ - { - id: "HolonymGovIdProvider", - weight: "16", - }, - ], - }, - ], - }, - { - header: "Biometrics verification", - platforms: [ - { - name: "Civic", - description:
TODO
, - documentationLink: "https://google.com", - credentials: [ - { - id: "CivicCaptchaPass", - weight: "1", - }, - { - id: "CivicUniquenessPass", - weight: "2", - }, - { - id: "CivicLivenessPass", - weight: "3", - }, - ], - }, - ], - }, - { - header: "Social & Professional Platforms", - platforms: [ - { - name: "LinkedIn", - description:
Claim Linkedin stamp
, - documentationLink: "https://google.com", - requireSignature: true, - oAuthPopup: true, - credentials: [ - { - id: "LinkedIn", - weight: "1", - }, - ], - }, - { - name: "Discord", - description:
Coming soon
, - documentationLink: "https://google.com", - requireSignature: true, - oAuthPopup: true, - credentials: [ - { - id: "Discord", - weight: "1", - }, - ], - }, - // { - // name: "Github", - // description:
Coming soon
, - // documentationLink: "https://google.com", - // requireSignature: true, - // oAuthPopup: true, - // credentials: [ - // { - // id: "Github", - // weight: "1", - // }, - // ], - // }, - { - name: "Google", - description:
Coming soon
, - documentationLink: "https://google.com", - requireSignature: true, - oAuthPopup: true, - credentials: [ - { - id: "Google", - weight: "1", - }, - ], - }, - ], - }, - { - header: VISIT_PASSPORT_HEADER, - platforms: [], - }, -].map((page) => ({ - ...page, - platforms: page.platforms.map((platform) => ({ - ...platform, - displayWeight: displayNumber( - platform.credentials.reduce( - (acc, credential) => acc + parseFloat(credential.weight), - 0 - ) - ), - })), -})); - export const ScoreTooLowBody = ({ generateSignatureCallback, }: { @@ -215,20 +57,6 @@ export const ScoreTooLowBody = ({ ); }; -const usePages = (pages: T[]) => { - const [idx, setIdx] = useState(0); - - const nextPage = () => setIdx((prev) => Math.min(prev + 1, pages.length - 1)); - const prevPage = () => setIdx((prev) => Math.max(prev - 1, 0)); - - const isFirstPage = idx === 0; - const isLastPage = idx === pages.length - 1; - - const page = pages[idx]; - - return { page, nextPage, prevPage, isFirstPage, isLastPage }; -}; - const ClaimedIcon = () => ( Promise; }) => { const { setSubtitle } = useHeaderControls(); - const { page, nextPage, prevPage, isFirstPage, isLastPage } = - usePages(STAMP_PAGES); - + const queryProps = useQueryContext(); + const { scorerId, apiKey, queryClient, overrideIamUrl } = queryProps; + const { page, nextPage, prevPage, isFirstPage, isLastPage, loading, error } = + usePaginatedStampPages({ + apiKey: apiKey, + scorerId: scorerId, + overrideIamUrl: overrideIamUrl, + queryClient: queryClient, + }); const [openPlatform, setOpenPlatform] = useState(null); - const { header, platforms } = page; - useEffect(() => { setSubtitle("VERIFY STAMPS"); - }); + }, [setSubtitle]); + + if (loading) return
Loading Stamps Metadata...
; + if (error) return
{error}
; + if (!page) return
No stamp metadata available
; + + const { header, platforms } = page; if (openPlatform) { - console.log("LARISA HELLO openPlatform", openPlatform); return ( & { queryClient: QueryClient; // This makes queryClient required }; @@ -28,7 +27,6 @@ export const QueryContextProvider = ({ scorerId, overrideIamUrl, challengeSignatureUrl, - oAuthPopUpUrl, queryClient, }: { children: React.ReactNode; @@ -40,7 +38,6 @@ export const QueryContextProvider = ({ scorerId, overrideIamUrl, challengeSignatureUrl, - oAuthPopUpUrl, // Use override if passed in, otherwise use the widget query client queryClient: queryClient || widgetQueryClient, }), @@ -50,7 +47,6 @@ export const QueryContextProvider = ({ scorerId, overrideIamUrl, challengeSignatureUrl, - oAuthPopUpUrl, queryClient, ] ); diff --git a/src/hooks/usePassportScore.tsx b/src/hooks/usePassportScore.tsx index e34b4bc..0fbc555 100644 --- a/src/hooks/usePassportScore.tsx +++ b/src/hooks/usePassportScore.tsx @@ -10,7 +10,7 @@ import axios from "axios"; import { useQueryContext } from "../contexts/QueryContext"; import { useCallback } from "react"; -const DEFAULT_IAM_URL = "https://embed.passport.xyz"; +export const DEFAULT_IAM_URL = "https://embed.passport.xyz"; export type PassportEmbedProps = { apiKey: string; @@ -21,7 +21,6 @@ export type PassportEmbedProps = { address?: string; overrideIamUrl?: string; challengeSignatureUrl?: string; - oAuthPopUpUrl?: string; // Optional, allows you to share a queryClient between the // widget(s) and the wider app queryClient?: QueryClient; diff --git a/src/hooks/useStampPages.tsx b/src/hooks/useStampPages.tsx new file mode 100644 index 0000000..3fb5207 --- /dev/null +++ b/src/hooks/useStampPages.tsx @@ -0,0 +1,93 @@ +import { useEffect, useState } from "react"; +import axios from "axios"; +import { DEFAULT_IAM_URL } from "./usePassportScore"; +import { QueryClient } from "@tanstack/react-query"; +export type Credential = { + id: string; + weight: string; +}; + +export type Platform = { + name: string; + description: string; + documentationLink: string; + requireSignature?: boolean; + requiresPopup?: boolean; + popUpUrl?: string; + credentials: Credential[]; + displayWeight: string; +}; + +export type StampPage = { + header: string; + platforms: Platform[]; +}; + +type StampsMetadataResponse = StampPage[]; + +export const usePaginatedStampPages = ({ + apiKey, + scorerId, + overrideIamUrl, + queryClient, +}: { + apiKey: string; + scorerId: string; + overrideIamUrl?: string; + queryClient?: QueryClient; +}) => { + const [stampPages, setStampPages] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + const [idx, setIdx] = useState(0); + + useEffect(() => { + const fetchStampPages = async () => { + try { + // TODO: fix this to use propr url encoding + const response = await axios.get( + `${overrideIamUrl || DEFAULT_IAM_URL}/embed/stamps/metadata?scorerId=${scorerId}`, + { + headers: { + "X-API-KEY": apiKey, + "Content-Type": "application/json", + }, + } + ); + + const data = response.data; + + // Convert description from HTML string to JSX + const formattedData = data.map((page: StampPage) => ({ + ...page, + platforms: page.platforms.map((platform) => ({ + ...platform, + description: platform.description, + displayWeight: platform.displayWeight, + })), + })); + + setStampPages(formattedData); + } catch (err) { + console.error("Error fetching stamp pages:", err); + setError("Failed to load stamp pages"); + } finally { + setLoading(false); + } + }; + + fetchStampPages(); + }, []); + + // Pagination controls + const nextPage = () => + setIdx((prev) => Math.min(prev + 1, stampPages.length - 1)); + const prevPage = () => setIdx((prev) => Math.max(prev - 1, 0)); + + const isFirstPage = idx === 0; + const isLastPage = idx === stampPages.length - 1; + + const page = stampPages[idx] ?? null; + + return { page, nextPage, prevPage, isFirstPage, isLastPage, loading, error }; +}; diff --git a/yarn.lock b/yarn.lock index b3a5882..7e32c93 100644 --- a/yarn.lock +++ b/yarn.lock @@ -120,6 +120,11 @@ axios@^1.7.7: form-data "^4.0.0" proxy-from-env "^1.1.0" +base64-js@^1.3.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + binary-extensions@^2.0.0: version "2.3.0" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.3.0.tgz#f6e14a97858d327252200242d4ccfe522c445522" @@ -142,6 +147,14 @@ browserslist@^4.23.3: node-releases "^2.0.18" update-browserslist-db "^1.1.1" +buffer@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" + integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.2.1" + caniuse-lite@^1.0.30001646, caniuse-lite@^1.0.30001669: version "1.0.30001675" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001675.tgz#0c1f01fc9cc543b61839753a4c234f995588d1b9" @@ -312,6 +325,11 @@ graceful-fs@^4.1.6, graceful-fs@^4.2.0: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== +ieee754@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + ignore@^5.2.4: version "5.3.2" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5"