Skip to content

Commit

Permalink
feat(app): adding scroll campaign mint page (#2941)
Browse files Browse the repository at this point in the history
* feat(app): adding scroll campaign mint page

* feat(app): moving attestation issuance to hook - wip

* chore(app): moved useAttestation to separate file

* feat: wip onchain cred formatting

* feat: vc normalization

* feat(iam): wip scroll dev badge endpoint

* wip

* cleanup, loading stamps from db

* feat(app): added scroll sepolia chain

* feat(iam): working current badge level check

* fix(app): fixed loading passport from DB

* fix(app): fix existing tests

* feat: styled wizard up to were sorry view

* fix(app): merge fixes

* test fix

* build fix

---------

Co-authored-by: schultztimothy <[email protected]>
  • Loading branch information
lucianHymer and tim-schultz authored Oct 9, 2024
1 parent d2b9a32 commit f72b51b
Show file tree
Hide file tree
Showing 35 changed files with 1,386 additions and 486 deletions.
8 changes: 3 additions & 5 deletions app/.env-example.env
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ NEXT_PUBLIC_PASSPORT_OP_RPC_URL=YOUR_RPC_URL
NEXT_PUBLIC_PASSPORT_ARB_RPC_URL=YOUR_RPC_URL
NEXT_PUBLIC_PASSPORT_ZKSYNC_RPC_URL=YOUR_RPC_URL
NEXT_PUBLIC_PASSPORT_SCROLL_RPC_URL=YOUR_RPC_URL
NEXT_PUBLIC_PASSPORT_SCROLL_SEPOLIA_RPC_URL=YOUR_RPC_URL
NEXT_PUBLIC_PASSPORT_SHAPE_RPC_URL=YOUR_RPC_URL

NEXT_PUBLIC_CERAMIC_CLIENT_URL=https://ceramic-clay.3boxlabs.com
Expand Down Expand Up @@ -91,9 +92,6 @@ NEXT_PUBLIC_FF_CERAMIC_CLIENT=off
NEXT_PUBLIC_ONBOARD_RESET_INDEX=1
NEXT_PUBLIC_GA_ID=id


# Env vars for the scroll campaign
NEXT_PUBLIC_SCROLL_CAMPAIGN_SELECTED_PROVIDERS='["DeveloperList#PassportCommiterLevel1#7f421a19","DeveloperList#PassportCommiterLevel2#0d74972f","DeveloperList#PassportCommiterLevel3#8c6b910f","DeveloperList#PassportCommiterLevel4#9381f80a"]'
NEXT_PUBLIC_SCROLL_CAMPAIGN_RPC_URL=https://scroll.drpc.org
NEXT_PUBLIC_SCROLL_CAMPAIGN_CONTRACT_ADDRESSES='["0x0000000000", "..."]'
NEXT_PUBLIC_SCROLL_CAMPAIGN_RPC_URL=https://scroll.drpc.org
NEXT_PUBLIC_SCROLL_BADGE_PROVIDER_INFO='[{"badgeContractAddress":"0x...","title":"Passport Developer","providers":[{"level":1,"name":"DeveloperList#PassportCommiterLevel1#7f421a19","image":"/assets/img1.svg"}]}]'
NEXT_PUBLIC_SCROLL_CAMPAIGN_CHAIN_ID=
36 changes: 26 additions & 10 deletions app/__tests__/components/SyncToChainButton.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { makeTestCeramicContext, renderWithContext } from "../../__test-fixtures
import { CeramicContextState } from "../../context/ceramicContext";
import { Chain } from "../../utils/chains";
import { ChakraProvider } from "@chakra-ui/react";
import { closeAllToasts } from "../../__test-fixtures__/toastTestHelpers";
import { switchNetworkMock } from "../../__mocks__/web3modalMock";

const mockWalletState = {
Expand Down Expand Up @@ -75,6 +76,8 @@ const chainWithEas = new Chain({
...chainConfig,
id: "0x14a33",
attestationProviderConfig: {
skipByDefault: false,
monochromeIcon: "/images/ethereum-icon.svg",
status: "enabled",
name: "Ethereum Attestation Service",
easScanUrl: "test.com",
Expand All @@ -84,26 +87,30 @@ const chainWithEas = new Chain({
const mockCeramicContext: CeramicContextState = makeTestCeramicContext();

describe("SyncToChainButton component", () => {
beforeEach(() => {
closeAllToasts();
});

it("should show coming soon if in active", async () => {
renderWithContext(
mockCeramicContext,
<SyncToChainButton onChainStatus={OnChainStatus.NOT_MOVED} chain={chainWithoutEas} />
<SyncToChainButton onChainStatus={OnChainStatus.NOT_MOVED} chain={chainWithoutEas} isLoading={false} />
);

expect(screen.getByText("Coming Soon")).toBeInTheDocument();
});
it("should be disabled if not active", async () => {
renderWithContext(
mockCeramicContext,
<SyncToChainButton onChainStatus={OnChainStatus.NOT_MOVED} chain={chainWithoutEas} />
<SyncToChainButton onChainStatus={OnChainStatus.NOT_MOVED} chain={chainWithoutEas} isLoading={false} />
);
const btn = screen.getByTestId("sync-to-chain-button");
expect(btn).toHaveAttribute("disabled");
});
it("should be disabled if up to date", async () => {
renderWithContext(
mockCeramicContext,
<SyncToChainButton onChainStatus={OnChainStatus.MOVED_UP_TO_DATE} chain={chainWithEas} />
<SyncToChainButton onChainStatus={OnChainStatus.MOVED_UP_TO_DATE} chain={chainWithEas} isLoading={false} />
);
const btn = screen.getByTestId("sync-to-chain-button");
expect(btn).toHaveAttribute("disabled");
Expand All @@ -113,15 +120,24 @@ describe("SyncToChainButton component", () => {
...chainConfig,
id: "0x123",
attestationProviderConfig: {
skipByDefault: false,
monochromeIcon: "/images/ethereum-icon.svg",
status: "enabled",
name: "Ethereum Attestation Service",
easScanUrl: "test.com",
},
});

jest
.spyOn(axios, "post")
.mockResolvedValueOnce({ data: { invalidCredentials: [], passport: [], signature: { v: 8, r: "s", s: "a" } } });

renderWithContext(
mockCeramicContext,
<SyncToChainButton onChainStatus={OnChainStatus.NOT_MOVED} chain={anotherChainWithEas} />
{
...mockCeramicContext,
passport: { ...mockCeramicContext.passport, stamps: [{ id: "test" } as any] },
},
<SyncToChainButton onChainStatus={OnChainStatus.NOT_MOVED} chain={anotherChainWithEas} isLoading={false} />
);
const btn = screen.getByTestId("sync-to-chain-button");
expect(btn).toHaveTextContent("Mint");
Expand All @@ -132,12 +148,12 @@ describe("SyncToChainButton component", () => {
renderWithContext(
{ ...mockCeramicContext },
<ChakraProvider>
<SyncToChainButton onChainStatus={OnChainStatus.NOT_MOVED} chain={chainWithEas} />
<SyncToChainButton onChainStatus={OnChainStatus.NOT_MOVED} chain={chainWithEas} isLoading={false} />
</ChakraProvider>
);
const btn = screen.getByTestId("sync-to-chain-button");
fireEvent.click(btn);
await screen.findByText("You do not have any Stamps to bring onchain.");
await screen.findByText("You do not have any Stamps to bring onchain.", { exact: false });
});

it("should render success toast if stamps are brought on chain", async () => {
Expand All @@ -150,15 +166,15 @@ describe("SyncToChainButton component", () => {
passport: { ...mockCeramicContext.passport, stamps: [{ id: "test" } as any] },
},
<ChakraProvider>
<SyncToChainButton onChainStatus={OnChainStatus.NOT_MOVED} chain={chainWithEas} />
<SyncToChainButton onChainStatus={OnChainStatus.NOT_MOVED} chain={chainWithEas} isLoading={false} />
</ChakraProvider>,
{},
{ threshold: 10, rawScore: 20 }
);
const btn = screen.getByTestId("sync-to-chain-button");
fireEvent.click(btn);

await screen.findByText("Passport submitted to chain.");
await screen.findByText("Attestation submitted to chain.");
});

it("should prompt user if score is low", async () => {
Expand All @@ -168,7 +184,7 @@ describe("SyncToChainButton component", () => {
passport: { ...mockCeramicContext.passport, stamps: [{ id: "test" } as any] },
},
<ChakraProvider>
<SyncToChainButton onChainStatus={OnChainStatus.NOT_MOVED} chain={chainWithEas} />
<SyncToChainButton onChainStatus={OnChainStatus.NOT_MOVED} chain={chainWithEas} isLoading={false} />
</ChakraProvider>,
{},
{ threshold: 15, rawScore: 10 }
Expand Down
10 changes: 1 addition & 9 deletions app/__tests__/utils/utils.test.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,5 @@
import { fetchPossibleEVMStamps, getTypesToCheck } from "../../signer/utils";
import { providers } from "@gitcoin/passport-platforms";
import {
CheckRequestBody,
CheckResponseBody,
Passport,
ProviderContext,
RequestPayload,
VerifiedPayload,
} from "@gitcoin/passport-types";
import { CheckResponseBody, Passport } from "@gitcoin/passport-types";
import { platforms } from "@gitcoin/passport-platforms";
const { Ens, Lens, Github } = platforms;
import axios from "axios";
Expand Down
25 changes: 3 additions & 22 deletions app/abi/PassportScoreScrollBadge.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@
"name": "badgeLevel",
"inputs": [
{
"name": "",
"type": "bytes32",
"internalType": "bytes32"
"name": "user",
"type": "address",
"internalType": "address"
}
],
"outputs": [
Expand Down Expand Up @@ -130,25 +130,6 @@
],
"stateMutability": "view"
},
{
"type": "function",
"name": "checkLevel",
"inputs": [
{
"name": "user",
"type": "address",
"internalType": "address"
}
],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "getAndValidateBadge",
Expand Down
9 changes: 1 addition & 8 deletions app/components/DashboardScorePanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { useAllOnChainStatus } from "../hooks/useOnChainStatus";
import { LoadButton } from "./LoadButton";
import { Hyperlink } from "@gitcoin/passport-platforms";
import { OnchainSidebar } from "./OnchainSidebar";
import { LoadingBar } from "./LoadingBar";

const PanelDiv = ({ className, children }: { className: string; children: React.ReactNode }) => {
return (
Expand Down Expand Up @@ -92,14 +93,6 @@ export const DashboardScorePanel = ({ className }: { className?: string }) => {
);
};

const LoadingBar = ({ className }: { className?: string }) => {
return (
<div
className={`h-10 w-full bg-size-400 animate-[loading-gradient_5s_ease-in-out_infinite] bg-gradient-to-r from-background via-foreground-5 to-background rounded-lg my-2 ${className}`}
/>
);
};

interface OnchainCTAProps {
setShowSidebar: (show: boolean) => void;
}
Expand Down
32 changes: 32 additions & 0 deletions app/components/LoadingBar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React from "react";

import { twMerge } from "tailwind-merge";

export const LoadingBar = ({ className }: { className?: string }) => {
return (
<div
className={twMerge(
"h-10 w-full bg-size-400 animate-[loading-gradient_5s_ease-in-out_infinite] bg-gradient-to-r from-background via-foreground-5 to-background rounded-lg my-2",
className
)}
/>
);
};

export type LoadingBarSectionProps = {
isLoading: boolean;
className?: string;
loadingBarClassName?: string;
children: React.ReactNode;
};

export const LoadingBarSection = ({ isLoading, className, loadingBarClassName, children }: LoadingBarSectionProps) => {
return (
<div className={twMerge("relative", className)}>
<LoadingBar
className={twMerge("absolute top-0 left-0", isLoading ? "visible" : "invisible", loadingBarClassName)}
/>
<div className={isLoading ? "invisible" : "visible"}>{children}</div>
</div>
);
};
Loading

0 comments on commit f72b51b

Please sign in to comment.