Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 3 additions & 6 deletions apps/connect/src/routes/login.lazy.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import GeoLogo from '@/assets/images/geo-logo-branded.svg?react';
import { AppTitle } from '@/components/ui/AppTitle';
import { Loading } from '@/components/ui/Loading';
import { Connect, type Identity } from '@graphprotocol/hypergraph';
import { type ConnectedWallet, useIdentityToken, usePrivy, useWallets } from '@privy-io/react-auth';
import { createLazyFileRoute, useRouter } from '@tanstack/react-router';
import { useCallback, useEffect, useState } from 'react';
import { createWalletClient, custom, type WalletClient } from 'viem';
import GeoLogo from '@/assets/images/geo-logo-branded.svg?react';
import { AppTitle } from '@/components/ui/AppTitle';
import { Loading } from '@/components/ui/Loading';

const CHAIN = import.meta.env.VITE_HYPERGRAPH_CHAIN === 'geogenesis' ? Connect.GEOGENESIS : Connect.GEO_TESTNET;
const syncServerUri = import.meta.env.VITE_HYPERGRAPH_SYNC_SERVER_ORIGIN;
Expand All @@ -25,7 +25,6 @@ function Login() {

const hypergraphLogin = useCallback(
async (walletClient: WalletClient, embeddedWallet: ConnectedWallet) => {
console.log('hypergraphLogin');
if (!identityToken) {
return;
}
Expand Down Expand Up @@ -69,14 +68,12 @@ function Login() {
);

useEffect(() => {
console.log('useEffect in login.lazy.tsx');
if (
!hypergraphLoginStarted && // avoid re-running the effect too often
privyAuthenticated && // privy must be authenticated to run it
walletsReady && // wallets must be ready to run it
wallets.length > 0 // wallets must have at least one wallet to run it
) {
console.log('running login effect');
setHypergraphLoginStarted(true);
(async () => {
try {
Expand Down
36 changes: 36 additions & 0 deletions apps/events/src/components/invite-to-space.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { useHypergraphApp, useSpace } from '@graphprotocol/hypergraph-react';
import { Button } from './ui/button';
import { Input } from './ui/input';

export function InviteToSpace() {
const { inviteToSpace } = useHypergraphApp();
const { ready: spaceReady, id: spaceId } = useSpace({ mode: 'private' });

if (!spaceReady) {
return <div>Loading space...</div>;
}

return (
<form
onSubmit={async (event) => {
event.preventDefault();
try {
console.log((event.target as HTMLFormElement).inviteeAddress.value);
const inviteeAddress = (event.target as HTMLFormElement).inviteeAddress.value;
await inviteToSpace({
space: spaceId,
inviteeAccountAddress: inviteeAddress,
});
alert('Invited to space');
(event.target as HTMLFormElement).inviteeAddress.value = '';
} catch (error) {
alert('Failed to invite to space');
console.error(error);
}
}}
>
<Input type="text" name="inviteeAddress" />
<Button type="submit">Invite to space</Button>
</form>
);
}
7 changes: 4 additions & 3 deletions apps/events/src/routes/index.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import { Input } from '@/components/ui/input';
import { store } from '@graphprotocol/hypergraph';
import { useHypergraphApp, useSpaces } from '@graphprotocol/hypergraph-react';
import { createFileRoute, Link } from '@tanstack/react-router';
import { useSelector } from '@xstate/store/react';
import { useEffect, useState } from 'react';
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import { Input } from '@/components/ui/input';

export const Route = createFileRoute('/')({
component: Index,
Expand Down Expand Up @@ -44,6 +44,7 @@ function Index() {
return (
<div className="flex flex-col gap-4 max-w-(--breakpoint-sm) mx-auto py-8">
<h2 className="text-lg font-bold">Invitations</h2>

{invitations.length === 0 && <div>No invitations</div>}
<ul className="text-xs">
{invitations.map((invitation) => {
Expand Down
24 changes: 3 additions & 21 deletions apps/events/src/routes/space/$spaceId/index.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { HypergraphSpaceProvider, useHypergraphApp } from '@graphprotocol/hypergraph-react';
import { createFileRoute } from '@tanstack/react-router';
import { useState } from 'react';
import { DevTool } from '@/components/dev-tool';
import { Todos } from '@/components/todos';
import { TodosReadOnly } from '@/components/todos-read-only';
import { TodosReadOnlyFilter } from '@/components/todos-read-only-filter';
import { Button } from '@/components/ui/button';
import { Users } from '@/components/users';
import { HypergraphSpaceProvider, useHypergraphApp } from '@graphprotocol/hypergraph-react';
import { createFileRoute } from '@tanstack/react-router';
import { useState } from 'react';

export const Route = createFileRoute('/space/$spaceId/')({
component: Space,
Expand All @@ -29,24 +29,6 @@ function Space() {
<TodosReadOnlyFilter />
<TodosReadOnly />
{show2ndTodos && <Todos />}
{/* <h3 className="text-xl font-bold">Invite people</h3>
<div className="flex flex-row gap-2">
{availableAccounts.map((invitee) => {
return (
<Button
key={invitee.accountAddress}
onClick={() => {
inviteToSpace({
space,
invitee: { accountAddress: getAddress(invitee.accountAddress) },
});
}}
>
Invite {invitee.accountAddress.substring(0, 6)}
</Button>
);
})}
</div> */}
<div className="mt-12 flex flex-row gap-2">
<DevTool spaceId={spaceId} />
<Button onClick={() => setShow2ndTodos((prevShow2ndTodos) => !prevShow2ndTodos)}>Toggle Todos</Button>
Expand Down
5 changes: 4 additions & 1 deletion apps/events/src/routes/space/$spaceId/playground.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { InviteToSpace } from '@/components/invite-to-space';
import { TodosPublic } from '@/components/todo/todos-public';
import { HypergraphSpaceProvider, useHypergraphApp } from '@graphprotocol/hypergraph-react';
import { createFileRoute } from '@tanstack/react-router';
import { TodosPublic } from '@/components/todo/todos-public';

export const Route = createFileRoute('/space/$spaceId/playground')({
component: PlaygroundRouteComponent,
Expand All @@ -17,6 +18,8 @@ function PlaygroundRouteComponent() {
return (
<div className="flex flex-col gap-4 max-w-(--breakpoint-sm) mx-auto py-8">
<HypergraphSpaceProvider space={spaceId}>
<InviteToSpace />

<TodosPublic />
</HypergraphSpaceProvider>
</div>
Expand Down
24 changes: 3 additions & 21 deletions apps/privy-login-example/src/routes/space/$spaceId/index.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { HypergraphSpaceProvider, useHypergraphApp } from '@graphprotocol/hypergraph-react';
import { createFileRoute } from '@tanstack/react-router';
import { useState } from 'react';
import { DevTool } from '@/components/dev-tool';
import { Todos } from '@/components/todos';
import { TodosReadOnly } from '@/components/todos-read-only';
import { TodosReadOnlyFilter } from '@/components/todos-read-only-filter';
import { Button } from '@/components/ui/button';
import { Users } from '@/components/users';
import { HypergraphSpaceProvider, useHypergraphApp } from '@graphprotocol/hypergraph-react';
import { createFileRoute } from '@tanstack/react-router';
import { useState } from 'react';

export const Route = createFileRoute('/space/$spaceId/')({
component: Space,
Expand All @@ -29,24 +29,6 @@ function Space() {
<TodosReadOnlyFilter />
<TodosReadOnly />
{show2ndTodos && <Todos />}
{/* <h3 className="text-xl font-bold">Invite people</h3>
<div className="flex flex-row gap-2">
{availableAccounts.map((invitee) => {
return (
<Button
key={invitee.accountAddress}
onClick={() => {
inviteToSpace({
space,
invitee: { accountAddress: getAddress(invitee.accountAddress) },
});
}}
>
Invite {invitee.accountAddress.substring(0, 6)}
</Button>
);
})}
</div> */}
<div className="mt-12 flex flex-row gap-2">
<DevTool spaceId={spaceId} />
<Button onClick={() => setShow2ndTodos((prevShow2ndTodos) => !prevShow2ndTodos)}>Toggle Todos</Button>
Expand Down
13 changes: 3 additions & 10 deletions apps/server/src/websocket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -259,17 +259,10 @@ export const WebSocketLayer = HttpLayerRouter.add(
keyBoxes: [], // No keyBoxes needed for accepting invitations
});

// Get the updated space data
const space = yield* spacesService.getSpace({
spaceId: request.spaceId,
accountAddress,
appIdentityAddress: address,
});

// Send the updated space back to the client
const outgoingMessage: Messages.ResponseSpace = {
type: 'space',
...space,
const outgoingMessage: Messages.ResponseInvitationAccepted = {
type: 'invitation-accepted',
invitationId: request.event.transaction.id,
};
yield* responseMailbox.offer(Messages.serializeV2(outgoingMessage));

Expand Down
4 changes: 1 addition & 3 deletions docs/docs/space-invitations.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@ const { inviteToSpace } = useHypergraphApp();

inviteToSpace({
space: "space-id",
invitee: {
accountAddress: "0x1234567890123456789012345678901234567890",
},
inviteeAccountAddress: "0x1234567890123456789012345678901234567890"
});
```

Expand Down
36 changes: 21 additions & 15 deletions packages/hypergraph-react/src/HypergraphAppContext.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
'use client';

import { automergeWasmBase64 } from '@automerge/automerge/automerge.wasm.base64';
import * as automerge from '@automerge/automerge/slim';
import type { DocHandle } from '@automerge/automerge-repo';
import { Repo } from '@automerge/automerge-repo/slim';
import { RepoContext } from '@automerge/automerge-repo-react-hooks';
import { Repo } from '@automerge/automerge-repo/slim';
import { automergeWasmBase64 } from '@automerge/automerge/automerge.wasm.base64';
import * as automerge from '@automerge/automerge/slim';
import { Graph } from '@graphprotocol/grc-20';
import {
Connect,
Expand Down Expand Up @@ -91,7 +91,7 @@ export type HypergraphAppCtx = {
listInvitations(): void;
acceptInvitation(params: Readonly<{ invitation: Messages.Invitation }>): Promise<unknown>;
subscribeToSpace(params: Readonly<{ spaceId: string }>): void;
inviteToSpace(params: Readonly<{ space: SpaceStorageEntry; invitee: { accountAddress: Address } }>): Promise<unknown>;
inviteToSpace(params: Readonly<{ space: string; inviteeAccountAddress: Address }>): Promise<unknown>;
getVerifiedIdentity(
accountAddress: string,
publicKey: string | null,
Expand Down Expand Up @@ -927,6 +927,13 @@ export function HypergraphAppProvider({
});
break;
}
case 'invitation-accepted': {
store.send({
type: 'invitationAccepted',
id: response.invitationId,
});
break;
}
default: {
Utils.assertExhaustive(response);
}
Expand Down Expand Up @@ -1313,16 +1320,14 @@ export function HypergraphAppProvider({

const inviteToSpace = useCallback(
async ({
space,
invitee,
space: spaceId,
inviteeAccountAddress,
}: Readonly<{
space: SpaceStorageEntry;
invitee: {
accountAddress: string;
};
space: string;
inviteeAccountAddress: Address;
}>) => {
if (!identity && !privyIdentity) {
throw new Error('No identity found');
throw new Error('No identity found');
}
const encryptionPrivateKey = identity?.encryptionPrivateKey || privyIdentity?.encryptionPrivateKey;
const encryptionPublicKey = identity?.encryptionPublicKey || privyIdentity?.encryptionPublicKey;
Expand All @@ -1338,12 +1343,13 @@ export function HypergraphAppProvider({
) {
throw new Error('Missing keys');
}
if (!space.state) {
console.error('No state found for space');
const space = store.getSnapshot().context.spaces.find((s) => s.id === spaceId);
if (space === undefined || space.state === undefined) {
console.error('No space or space state found', spaceId);
return;
}
const inviteeWithKeys = await Identity.getVerifiedIdentity(
invitee.accountAddress,
inviteeAccountAddress,
null,
appId,
syncServerUri,
Expand Down Expand Up @@ -1378,7 +1384,7 @@ export function HypergraphAppProvider({
ciphertext: Utils.bytesToHex(keyBox.keyBoxCiphertext),
nonce: Utils.bytesToHex(keyBox.keyBoxNonce),
authorPublicKey: encryptionPublicKey,
accountAddress: invitee.accountAddress,
accountAddress: inviteeAccountAddress,
};
});

Expand Down
1 change: 0 additions & 1 deletion packages/hypergraph/src/connect/parse-auth-params.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ export const parseAuthParams = (
}

try {
console.log('data', data);
const result = decodePayload(data as ConnectAuthPayload);

if (Either.isLeft(result)) {
Expand Down
8 changes: 8 additions & 0 deletions packages/hypergraph/src/messages/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,13 @@ export const ResponseUpdateConfirmed = Schema.Struct({
spaceId: Schema.String,
});

export const ResponseInvitationAccepted = Schema.Struct({
type: Schema.Literal('invitation-accepted'),
invitationId: Schema.String,
});

export type ResponseInvitationAccepted = Schema.Schema.Type<typeof ResponseInvitationAccepted>;

export type ResponseUpdateConfirmed = Schema.Schema.Type<typeof ResponseUpdateConfirmed>;

export const ResponseUpdatesNotification = Schema.Struct({
Expand Down Expand Up @@ -433,6 +440,7 @@ export const ResponseMessage = Schema.Union(
ResponseAccountInboxMessage,
ResponseAccountInboxMessages,
ResponseAccountInboxes,
ResponseInvitationAccepted,
);

export type ResponseMessage = Schema.Schema.Type<typeof ResponseMessage>;
Expand Down
7 changes: 7 additions & 0 deletions packages/hypergraph/src/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ const initialStoreContext: StoreContext = {

type StoreEvent =
| { type: 'setInvitations'; invitations: Invitation[] }
| { type: 'invitationAccepted'; id: string }
| { type: 'setMapping'; mapping: Mapping }
| { type: 'reset' }
| { type: 'addUpdateInFlight'; updateId: string }
Expand Down Expand Up @@ -171,6 +172,12 @@ export const store: Store<StoreContext, StoreEvent, GenericEventObject> = create
invitations: event.invitations,
};
},
invitationAccepted: (context, event: { id: string }) => {
return {
...context,
invitations: context.invitations.filter((invitation) => invitation.id !== event.id),
};
},
setMapping: (context, event: { mapping: Mapping }) => {
return {
...context,
Expand Down
Loading