forked from rainymind/gitcoin_passport
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
1479 on chain sidebar (passportxyz#1516)
* chore(app): pulled lateston chain button * feat(app): on chain sidebar markup * feat(app): on chain logos * feat(app): chain status * feat(app): updates card borders and adds tests * chore(app): test onchain - db provider check --------- Co-authored-by: schultztimothy <[email protected]>
- Loading branch information
1 parent
ed96a23
commit bbca98e
Showing
15 changed files
with
349 additions
and
25 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
import React from "react"; | ||
import { screen } from "@testing-library/react"; | ||
|
||
import { NetworkCard, checkOnChainStatus, OnChainStatus } from "../../components/NetworkCard"; | ||
|
||
import { | ||
makeTestCeramicContext, | ||
makeTestUserContext, | ||
renderWithContext, | ||
} from "../../__test-fixtures__/contextTestHelpers"; | ||
|
||
import { UserContextState } from "../../context/userContext"; | ||
import { CeramicContextState, AllProvidersState, ProviderState } from "../../context/ceramicContext"; | ||
import { OnChainProviderType } from "../../context/onChainContext"; | ||
import { Drawer, DrawerOverlay } from "@chakra-ui/react"; | ||
|
||
jest.mock("../../utils/onboard.ts"); | ||
|
||
jest.mock("next/router", () => ({ | ||
useRouter: () => ({ | ||
query: { filter: "" }, | ||
}), | ||
})); | ||
|
||
const chains = [ | ||
{ | ||
id: "12345", | ||
token: "SEP", | ||
label: "Sepolia Testnet", | ||
rpcUrl: "http://www.sepolia.com", | ||
icon: "sepolia.svg", | ||
}, | ||
{ | ||
id: "67899", | ||
token: "ETH", | ||
label: "Ethereum Testnet", | ||
rpcUrl: "http://www.etherum.com", | ||
icon: "ethereum.svg", | ||
}, | ||
]; | ||
|
||
const mockUserContext: UserContextState = makeTestUserContext(); | ||
const mockCeramicContext: CeramicContextState = makeTestCeramicContext(); | ||
|
||
describe("OnChainSidebar", () => { | ||
it("renders", () => { | ||
const drawer = () => ( | ||
<Drawer isOpen={true} placement="right" size="sm" onClose={() => {}}> | ||
<DrawerOverlay /> | ||
<NetworkCard key={4} chain={chains[0]} activeChains={[chains[0].id, chains[1].id]} /> | ||
</Drawer> | ||
); | ||
renderWithContext(mockUserContext, mockCeramicContext, drawer()); | ||
expect(screen.getByText("Sepolia Testnet")).toBeInTheDocument(); | ||
}); | ||
}); | ||
|
||
describe("checkOnChainStatus", () => { | ||
const mockAllProvidersState: AllProvidersState = { | ||
["Google"]: { | ||
stamp: { | ||
provider: "Google", | ||
credential: { | ||
credentialSubject: { | ||
hash: "hash1", | ||
}, | ||
}, | ||
}, | ||
} as unknown as ProviderState, | ||
["Ens"]: { | ||
stamp: { | ||
provider: "Ens", | ||
credential: { | ||
credentialSubject: { | ||
hash: "hash2", | ||
}, | ||
}, | ||
}, | ||
} as unknown as ProviderState, | ||
}; | ||
|
||
const mockOnChainProviders: OnChainProviderType[] = [ | ||
{ | ||
providerName: "Google", | ||
credentialHash: "hash1", | ||
expirationDate: new Date(), | ||
issuanceDate: new Date(), | ||
}, | ||
{ | ||
providerName: "Ens", | ||
credentialHash: "hash2", | ||
expirationDate: new Date(), | ||
issuanceDate: new Date(), | ||
}, | ||
]; | ||
|
||
it("should return NOT_MOVED when onChainProviders is an empty array", () => { | ||
expect(checkOnChainStatus(mockAllProvidersState, [])).toBe(OnChainStatus.NOT_MOVED); | ||
}); | ||
|
||
it("should return MOVED_UP_TO_DATE when onChainProviders matches with allProvidersState", () => { | ||
expect(checkOnChainStatus(mockAllProvidersState, mockOnChainProviders)).toBe(OnChainStatus.MOVED_UP_TO_DATE); | ||
}); | ||
|
||
it("should return MOVED_OUT_OF_DATE when there are differences between onChainProviders and allProvidersState", () => { | ||
const diffMockAllProviderState: AllProvidersState = { | ||
...mockAllProvidersState, | ||
["Github"]: { | ||
stamp: { | ||
provider: "Github", | ||
credential: { | ||
credentialSubject: { | ||
hash: "hash2", | ||
}, | ||
}, | ||
}, | ||
} as unknown as ProviderState, | ||
}; | ||
expect(checkOnChainStatus(diffMockAllProviderState, mockOnChainProviders)).toBe(OnChainStatus.MOVED_OUT_OF_DATE); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
import { Stamp } from "@gitcoin/passport-types"; | ||
import { useContext, useEffect, useState } from "react"; | ||
import { CeramicContext, AllProvidersState, ProviderState } from "../context/ceramicContext"; | ||
import { OnChainContext, OnChainProviderType } from "../context/onChainContext"; | ||
|
||
type Chain = { | ||
id: string; | ||
token: string; | ||
label: string; | ||
rpcUrl: string; | ||
icon: string; | ||
}; | ||
|
||
export enum OnChainStatus { | ||
NOT_MOVED, | ||
MOVED_OUT_OF_DATE, | ||
MOVED_UP_TO_DATE, | ||
} | ||
|
||
type ProviderWithStamp = ProviderState & { stamp: Stamp }; | ||
|
||
export const checkOnChainStatus = ( | ||
allProvidersState: AllProvidersState, | ||
onChainProviders: OnChainProviderType[] | ||
): OnChainStatus => { | ||
if (onChainProviders.length === 0) { | ||
return OnChainStatus.NOT_MOVED; | ||
} | ||
const verifiedDbProviders: ProviderWithStamp[] = Object.values(allProvidersState).filter( | ||
(provider): provider is ProviderWithStamp => provider.stamp !== undefined | ||
); | ||
|
||
const onChainDifference = verifiedDbProviders.filter( | ||
(provider) => | ||
!onChainProviders.some( | ||
(onChainProvider) => | ||
onChainProvider.providerName === provider.stamp.provider && | ||
onChainProvider.credentialHash === provider.stamp.credential.credentialSubject?.hash | ||
) | ||
); | ||
|
||
return onChainDifference.length > 0 ? OnChainStatus.MOVED_OUT_OF_DATE : OnChainStatus.MOVED_UP_TO_DATE; | ||
}; | ||
|
||
export function getButtonMsg(onChainStatus: OnChainStatus): string { | ||
switch (onChainStatus) { | ||
case OnChainStatus.NOT_MOVED: | ||
return "Up to date"; | ||
case OnChainStatus.MOVED_OUT_OF_DATE: | ||
return "Update"; | ||
case OnChainStatus.MOVED_UP_TO_DATE: | ||
return "Up to date"; | ||
} | ||
} | ||
|
||
export function NetworkCard({ chain, activeChains }: { chain: Chain; activeChains: string[] }) { | ||
const { allProvidersState } = useContext(CeramicContext); | ||
const { onChainProviders } = useContext(OnChainContext); | ||
const [isActive, setIsActive] = useState(false); | ||
const [onChainStatus, setOnChainStatus] = useState<OnChainStatus>(OnChainStatus.NOT_MOVED); | ||
|
||
useEffect(() => { | ||
setIsActive(activeChains.includes(chain.id)); | ||
}, [activeChains, chain.id]); | ||
|
||
useEffect(() => { | ||
const checkStatus = async () => { | ||
const stampStatus = await checkOnChainStatus(allProvidersState, onChainProviders); | ||
setOnChainStatus(stampStatus); | ||
}; | ||
checkStatus(); | ||
}, [allProvidersState, onChainProviders]); | ||
|
||
return ( | ||
<div className="border border-accent-2 bg-background-2 p-0"> | ||
<div className="mx-4 my-2"> | ||
<div className="flex w-full"> | ||
<div className="mr-4"> | ||
<img className="max-h-6" src={chain.icon} alt={`${chain.label} logo`} /> | ||
</div> | ||
<div> | ||
<div className="flex w-full flex-col"> | ||
<h1 className="text-lg text-color-1">{chain.label}</h1> | ||
<p className="mt-2 text-color-4 md:inline-block">Not moved yet</p> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
<button className="verify-btn center" data-testid="card-menu-button"> | ||
<span className="mx-2 translate-y-[1px] text-muted"> | ||
{isActive ? getButtonMsg(onChainStatus) : "Coming Soon"} | ||
</span> | ||
</button> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
import { Drawer, DrawerContent, DrawerHeader, DrawerBody, DrawerOverlay, DrawerCloseButton } from "@chakra-ui/react"; | ||
import { useEffect, useState } from "react"; | ||
import { chains } from "../utils/onboard"; | ||
import { NetworkCard } from "./NetworkCard"; | ||
|
||
type OnchainSidebarProps = { | ||
isOpen: boolean; | ||
onClose: () => void; | ||
}; | ||
|
||
export function OnchainSidebar({ isOpen, onClose }: OnchainSidebarProps) { | ||
const activeOnChainPassportChains = process.env.NEXT_PUBLIC_ACTIVE_ON_CHAIN_PASSPORT_CHAINIDS; | ||
const [activeChains, setActiveChains] = useState<string[]>([]); | ||
|
||
useEffect(() => { | ||
if (activeOnChainPassportChains) { | ||
const chainsArray = JSON.parse(activeOnChainPassportChains); | ||
setActiveChains(chainsArray); | ||
} | ||
}, [activeOnChainPassportChains]); | ||
|
||
return ( | ||
<Drawer isOpen={isOpen} placement="right" size="sm" onClose={onClose}> | ||
<DrawerOverlay /> | ||
<DrawerContent | ||
style={{ | ||
backgroundColor: "var(--color-background-2)", | ||
border: "1px solid var(--color-accent-2)", | ||
borderRadius: "6px", | ||
}} | ||
> | ||
<DrawerCloseButton className="text-color-1" /> | ||
<DrawerHeader className="text-center text-color-1"> | ||
<div className="mt-10 justify-center"> | ||
<h2 className="mt-4 text-2xl">Go On-Chain</h2> | ||
<p className="text-base font-normal"> | ||
Moving your passport on-chain creates a tamper-proof record of your stamps. This is only required if | ||
you're using applications that fetch Passport data from on-chain. Note: This involves blockchain | ||
network fees and a $2 Gitcoin minting fee. | ||
</p> | ||
</div> | ||
</DrawerHeader> | ||
<DrawerBody> | ||
{chains.map((chain) => ( | ||
<NetworkCard key={chain.id} chain={chain} activeChains={activeChains} /> | ||
))} | ||
</DrawerBody> | ||
</DrawerContent> | ||
</Drawer> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.