Skip to content

Commit 033c8db

Browse files
committed
Merge branch 'main' of github.com:graphprotocol/hypergraph into chris.whited/feat-react-pkg
2 parents 48aa23a + 98d553a commit 033c8db

File tree

7 files changed

+124
-150
lines changed

7 files changed

+124
-150
lines changed

apps/events/src/Boot.tsx

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { RouterProvider, createRouter } from '@tanstack/react-router';
22

3-
import { AuthProvider } from './components/auth';
3+
import { Identity } from '@graphprotocol/hypergraph';
4+
import { PrivyProvider } from '@privy-io/react-auth';
45
import { routeTree } from './routeTree.gen';
56

67
// Create a new router instance
@@ -15,8 +16,25 @@ declare module '@tanstack/react-router' {
1516

1617
export function Boot() {
1718
return (
18-
<AuthProvider>
19-
<RouterProvider router={router} />
20-
</AuthProvider>
19+
<PrivyProvider // note: PrivyProvider is only needed for the login page and the logout button in the navigation
20+
appId="cm4wx6ziv00ngrmfjf9ik36iu"
21+
config={{
22+
// Display email and wallet as login methods
23+
loginMethods: ['email', 'wallet', 'google', 'twitter', 'github'],
24+
// Customize Privy's appearance in your app
25+
appearance: {
26+
theme: 'light',
27+
accentColor: '#676FFF',
28+
},
29+
// Create embedded wallets for users who don't have a wallet
30+
embeddedWallets: {
31+
createOnLogin: 'users-without-wallets',
32+
},
33+
}}
34+
>
35+
<Identity.GraphLogin storage={localStorage}>
36+
<RouterProvider router={router} />
37+
</Identity.GraphLogin>
38+
</PrivyProvider>
2139
);
2240
}

apps/events/src/components/auth.tsx

Lines changed: 0 additions & 92 deletions
This file was deleted.

apps/events/src/components/logout.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ export function Logout() {
77
const { logout: graphLogout } = Auth.useHypergraphAuth();
88
const { logout: privyLogout } = usePrivy();
99
const router = useRouter();
10-
const disconnectWallet = () => {
11-
graphLogout();
12-
privyLogout();
10+
const disconnectWallet = async () => {
11+
await privyLogout();
12+
graphLogout(); // needs to be called after privy logout since it triggers a re-render
1313
router.navigate({
1414
to: '/login',
1515
});

apps/events/src/routes/__root.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export const Route = createRootRoute({
2828
<>
2929
<div className="flex flex-col min-h-screen">
3030
<header className="px-4 lg:px-6 h-14 flex items-center">
31-
<Link to="/" className="flex items-center justify-center">
31+
<Link to={authenticated ? '/' : '/login'} className="flex items-center justify-center">
3232
Home
3333
</Link>
3434
<nav className="ml-auto flex gap-4 sm:gap-6">

apps/events/src/routes/index.tsx

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,10 @@ import { store } from '@graphprotocol/hypergraph';
22
import { Hypergraph } from '@graphprotocol/hypergraph-react';
33
import { Link, createFileRoute } from '@tanstack/react-router';
44
import { useSelector } from '@xstate/store/react';
5+
import { useEffect } from 'react';
56

67
import { Button } from '@/components/ui/button';
78
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
8-
import { useEffect } from 'react';
9-
10-
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
11-
type UnknownRoute = any;
129

1310
export const Route = createFileRoute('/')({
1411
component: Index,
@@ -78,7 +75,7 @@ function Index() {
7875
{spaces.map((space) => {
7976
return (
8077
<li key={space.id}>
81-
<Link to={`/space/${space.id}` as UnknownRoute}>
78+
<Link to="/space/$spaceId" params={{ spaceId: space.id }}>
8279
<Card>
8380
<CardHeader>
8481
<CardTitle>{space.id}</CardTitle>
Lines changed: 93 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
1+
import type { Identity } from '@graphprotocol/hypergraph';
12
import { Auth } from '@graphprotocol/hypergraph-react';
2-
import { usePrivy } from '@privy-io/react-auth';
3-
import { Link, createLazyFileRoute } from '@tanstack/react-router';
3+
import { usePrivy, useWallets } from '@privy-io/react-auth';
4+
import { createLazyFileRoute, useRouter } from '@tanstack/react-router';
5+
import { Loader2 } from 'lucide-react';
6+
import { useEffect, useState } from 'react';
7+
import { createWalletClient, custom } from 'viem';
8+
import { mainnet } from 'viem/chains';
49

510
import { Button } from '@/components/ui/button';
611
import { availableAccounts } from '@/lib/availableAccounts';
@@ -10,52 +15,98 @@ export const Route = createLazyFileRoute('/login')({
1015
});
1116

1217
function Login() {
13-
const { ready, authenticated, login } = usePrivy();
14-
// Disable login when Privy is not ready or the user is already authenticated
15-
const disableLogin = !ready || (ready && authenticated);
16-
const { setIdentityAndSessionToken, authenticated: graphAuthenticated } = Auth.useHypergraphAuth();
18+
const { ready: privyReady, login: privyLogin, signMessage, authenticated: privyAuthenticated } = usePrivy();
19+
const { ready: walletsReady, wallets } = useWallets();
20+
const { setIdentityAndSessionToken, login: hypergraphLogin } = Auth.useHypergraphAuth();
21+
const { navigate } = useRouter();
22+
const [hypergraphLoginStarted, setHypergraphLoginStarted] = useState(false);
23+
24+
useEffect(() => {
25+
if (
26+
!hypergraphLoginStarted && // avoid re-running the effect to often
27+
privyAuthenticated && // privy must be authenticated to run it
28+
walletsReady && // wallets must be ready to run it
29+
wallets.length > 0 // wallets must have at least one wallet to run it
30+
) {
31+
setHypergraphLoginStarted(true);
32+
(async () => {
33+
try {
34+
const embeddedWallet = wallets.find((wallet) => wallet.walletClientType === 'privy') || wallets[0];
35+
const privyProvider = await embeddedWallet.getEthereumProvider();
36+
const walletClient = createWalletClient({
37+
chain: mainnet,
38+
transport: custom(privyProvider),
39+
});
40+
41+
const signer: Identity.Signer = {
42+
getAddress: async () => {
43+
const [address] = await walletClient.getAddresses();
44+
return address;
45+
},
46+
signMessage: async (message: string) => {
47+
if (embeddedWallet.walletClientType === 'privy') {
48+
const { signature } = await signMessage({ message });
49+
return signature;
50+
}
51+
const [address] = await walletClient.getAddresses();
52+
return await walletClient.signMessage({ account: address, message });
53+
},
54+
};
55+
56+
await hypergraphLogin(signer);
57+
navigate({ to: '/' });
58+
} catch (error) {
59+
alert('Failed to login');
60+
console.error(error);
61+
}
62+
})();
63+
}
64+
}, [hypergraphLoginStarted, walletsReady, wallets, signMessage, hypergraphLogin, navigate, privyAuthenticated]);
1765

1866
return (
1967
<div className="flex flex-1 justify-center items-center flex-col gap-4">
20-
{(!ready || !authenticated) && !graphAuthenticated ? (
68+
<div>
69+
<Button
70+
disabled={!privyReady || hypergraphLoginStarted}
71+
onClick={(event) => {
72+
event.preventDefault();
73+
privyLogin({});
74+
}}
75+
>
76+
Log in {hypergraphLoginStarted && <Loader2 className="ml-2 w-4 h-4 animate-spin" />}
77+
</Button>
78+
2179
<div>
22-
<Button disabled={disableLogin} onClick={login}>
23-
Log in
80+
<h1>Choose account</h1>
81+
<Button
82+
onClick={(event) => {
83+
event.preventDefault();
84+
setIdentityAndSessionToken(availableAccounts[0]);
85+
navigate({ to: '/' });
86+
}}
87+
>
88+
{availableAccounts[0].accountId.substring(0, 6)}
89+
</Button>
90+
<Button
91+
onClick={(event) => {
92+
event.preventDefault();
93+
setIdentityAndSessionToken(availableAccounts[1]);
94+
navigate({ to: '/' });
95+
}}
96+
>
97+
{availableAccounts[1].accountId.substring(0, 6)}
98+
</Button>
99+
<Button
100+
onClick={(event) => {
101+
event.preventDefault();
102+
setIdentityAndSessionToken(availableAccounts[2]);
103+
navigate({ to: '/' });
104+
}}
105+
>
106+
{availableAccounts[2].accountId.substring(0, 6)}
24107
</Button>
25-
26-
<div>
27-
<h1>Choose account</h1>
28-
<Button
29-
onClick={(event) => {
30-
event.preventDefault();
31-
setIdentityAndSessionToken(availableAccounts[0]);
32-
}}
33-
>
34-
{availableAccounts[0].accountId.substring(0, 6)}
35-
</Button>
36-
<Button
37-
onClick={(event) => {
38-
event.preventDefault();
39-
setIdentityAndSessionToken(availableAccounts[1]);
40-
}}
41-
>
42-
{availableAccounts[1].accountId.substring(0, 6)}
43-
</Button>
44-
<Button
45-
onClick={(event) => {
46-
event.preventDefault();
47-
setIdentityAndSessionToken(availableAccounts[2]);
48-
}}
49-
>
50-
{availableAccounts[2].accountId.substring(0, 6)}
51-
</Button>
52-
</div>
53108
</div>
54-
) : (
55-
<Link className="text-blue-500 hover:text-blue-600 underline" to="/">
56-
Go to Home
57-
</Link>
58-
)}
109+
</div>
59110
</div>
60111
);
61112
}

packages/hypergraph-react/src/HypergraphAuthContext.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export type HypergraphAuthCtx = {
1414
getAccountId(): string | null;
1515
getIdentity(): Identity.Identity | null;
1616
authenticated: boolean;
17-
login(): void;
17+
login(signer: Identity.Signer): Promise<void>;
1818
logout(): void;
1919
setIdentityAndSessionToken(account: Identity.Identity & { sessionToken: string }): void;
2020
};
@@ -30,7 +30,7 @@ export const HypergraphAuthContext = createContext<HypergraphAuthCtx>({
3030
return null;
3131
},
3232
authenticated: false,
33-
login() {},
33+
async login() {},
3434
logout() {},
3535
setIdentityAndSessionToken() {},
3636
});
@@ -250,7 +250,7 @@ export function HypergraphAuthProvider({
250250
Identity.storeKeys(storage, accountId, keys);
251251
}
252252

253-
async function login() {
253+
async function login(signer: Identity.Signer) {
254254
if (!signer) {
255255
return;
256256
}

0 commit comments

Comments
 (0)