Skip to content
Merged
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
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
22
1 change: 1 addition & 0 deletions apps/connect/.prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
routeTree.gen.ts
20 changes: 0 additions & 20 deletions apps/connect/components.json

This file was deleted.

4 changes: 2 additions & 2 deletions apps/connect/index.html
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Connect</title>
<title>Geo Connect</title>
<meta name="description" content="Authenticate with your Geo Account" />
</head>
<body>
Expand Down
23 changes: 14 additions & 9 deletions apps/connect/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,39 +5,44 @@
"type": "module",
"scripts": {
"dev": "vite --force",
"preview": "vite preview"
"preview": "vite preview",
"lint": "biome check --formatter-enabled=false",
"lint:fix": "biome check --formatter-enabled=false --write --unsafe",
"format": "prettier --write --list-different . && biome format --write",
"format:check": "biome check --linter-enabled=false",
"check": "pnpm run lint && pnpm run format:check",
"check:fix": "pnpm run lint:fix && pnpm run format"
},
"dependencies": {
"@base-ui-components/react": "1.0.0-beta.0",
"@graphprotocol/hypergraph": "workspace:*",
"@graphprotocol/hypergraph-react": "workspace:*",
"@privy-io/react-auth": "^2.13.0",
"@radix-ui/react-avatar": "^1.1.9",
"@radix-ui/react-icons": "^1.3.2",
"@radix-ui/react-slot": "^1.2.2",
"@tanstack/react-query": "^5.75.5",
"@tanstack/react-router": "^1.120.2",
"@xstate/store": "^3.5.1",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"effect": "^3.16.3",
"framer-motion": "^12.10.1",
"graphql-request": "^7.2.0",
"lucide-react": "^0.508.0",
"react": "^19.1.0",
"react-dom": "^19.1.0",
"tailwind-merge": "^3.2.0",
"tailwindcss-animate": "^1.0.7",
"viem": "^2.30.6",
"vite": "^6.3.5"
},
"devDependencies": {
"@tailwindcss/vite": "^4.1.5",
"@tailwindcss/vite": "^4.1.10",
"@tanstack/router-devtools": "^1.120.2",
"@tanstack/router-plugin": "^1.120.2",
"@types/node": "^22.15.15",
"@types/react": "^19.1.3",
"@types/react-dom": "^19.1.3",
"@vitejs/plugin-react": "^4.4.1",
"tailwindcss": "^4.1.5"
"prettier": "^3.6.0",
"prettier-plugin-tailwindcss": "^0.6.13",
"tailwindcss": "^4.1.10",
"unplugin-fonts": "^1.3.1",
"vite-plugin-svgr": "^4.3.0"
}
}
12 changes: 12 additions & 0 deletions apps/connect/prettier.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/**
* We only use Prettier to sort Tailwind classes; Biome handles the rest.
* When Biome's `useSortedClasses` is on par with `prettier-plugin-tailwindcss`, we can switch to it and remove Prettier completely.
* See https://biomejs.dev/linter/rules/use-sorted-classes/
*/
export default {
singleQuote: true,
printWidth: 120,
plugins: ['prettier-plugin-tailwindcss'],
tailwindPreserveWhitespace: true,
tailwindFunctions: ['cn'],
};
2 changes: 1 addition & 1 deletion apps/connect/src/Boot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export function Boot() {
loginMethods: ['email', 'google'],
appearance: {
theme: 'light',
accentColor: '#676FFF',
accentColor: '#6833ff',
},
embeddedWallets: {
createOnLogin: 'users-without-wallets',
Expand Down
Binary file added apps/connect/src/assets/fonts/Calibre-Bold.otf
Binary file not shown.
Binary file added apps/connect/src/assets/fonts/Calibre-Medium.otf
Binary file not shown.
Binary file not shown.
Binary file not shown.
10 changes: 10 additions & 0 deletions apps/connect/src/assets/images/geo-logo-branded.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions apps/connect/src/assets/images/geo-logo-mono.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { Loading } from '@/components/ui/Loading';
import { cn } from '@/lib/utils';
import { Key, type Messages, SpaceEvents, SpaceInfo, StoreConnect, Utils } from '@graphprotocol/hypergraph';
import { useIdentityToken } from '@privy-io/react-auth';
import { useQueryClient } from '@tanstack/react-query';
import { useSelector } from '@xstate/store/react';
import { Effect } from 'effect';
import { useState } from 'react';
import { Spinner } from './spinner';
import { Button } from './ui/button';
import { Input } from './ui/input';

export function CreateSpace() {
interface CreateSpaceCardProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'children'> {}

export function CreateSpaceCard({ className, ...props }: CreateSpaceCardProps) {
const [isLoading, setIsLoading] = useState(false);
const [spaceName, setSpaceName] = useState('');
const { identityToken } = useIdentityToken();
Expand All @@ -19,7 +20,11 @@ export function CreateSpace() {
const createSpace = async () => {
setIsLoading(true);
if (!accountAddress || !keys || !identityToken) {
console.error('Missing required fields', { accountAddress, keys, identityToken });
console.error('Missing required fields', {
accountAddress,
keys,
identityToken,
});
setIsLoading(false);
return;
}
Expand Down Expand Up @@ -93,14 +98,22 @@ export function CreateSpace() {
};

return (
<div className="flex flex-col gap-2">
<span className="text-xs text-gray-500">Create a new space</span>
<div className="flex flex-row gap-2 items-center">
<Input value={spaceName} onChange={(e) => setSpaceName(e.target.value)} />
<Button className="home-button" onClick={createSpace} disabled={isLoading}>
Create Space {isLoading ? <Spinner /> : null}
</Button>
</div>
<div className={cn('c-card', className)} {...props}>
<h2 className="c-card-title">Create a new space</h2>
<form className="flex gap-2">
<input
type="text"
placeholder="My cool space"
value={spaceName}
onChange={(e) => setSpaceName(e.target.value)}
required
className="c-input grow"
/>
<button type="submit" disabled={isLoading} onClick={createSpace} className="c-button shrink-0">
Create Space
{isLoading ? <Loading hideLabel /> : null}
</button>
</form>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { Loading } from '@/components/ui/Loading';
import { usePrivy } from '@privy-io/react-auth';
import { useRouter } from '@tanstack/react-router';
import { Loader2 } from 'lucide-react';
import { useState } from 'react';
import { Button } from './ui/button';

export function Logout() {
export function LogoutButton() {
const { logout: privyLogout, ready, authenticated } = usePrivy();
const router = useRouter();
const [isLoading, setIsLoading] = useState(false);
Expand All @@ -18,14 +17,14 @@ export function Logout() {
};

return (
<Button className="home-button" onClick={() => disconnectWallet()} disabled={!ready || !authenticated}>
{isLoading ? (
<>
<Loader2 className="w-4 h-4 animate-spin" /> Logout
</>
) : (
'Logout'
)}
</Button>
<button
type="button"
disabled={!ready || !authenticated}
onClick={() => disconnectWallet()}
className="c-button c-button--small"
>
{isLoading ? <Loading hideLabel /> : null}
Log out
</button>
);
}
123 changes: 123 additions & 0 deletions apps/connect/src/components/SpacesCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import { Loading } from '@/components/ui/Loading';
import type { PrivateSpaceData } from '@/hooks/use-private-spaces';
import type { PublicSpaceData } from '@/hooks/use-public-spaces';
import { cn } from '@/lib/utils';
import { Popover } from '@base-ui-components/react/popover';

interface SpacesCardProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'children'> {
spaces: (PublicSpaceData | PrivateSpaceData)[];
status?: 'loading' | { error: boolean | string } | undefined;
}

export function SpacesCard({ spaces, status, className, ...props }: SpacesCardProps) {
const error =
typeof status === 'object' && 'error' in status
? typeof status.error === 'boolean'
? status.error
: status.error || true
: false;
return (
<div
className={cn(
`group/card c-card scroll-y scrollbar-none
has-data-error:bg-error-dark
has-data-error:text-error-light
flex flex-col`,
className,
)}
{...props}
>
<h2
className={`
c-card-title group-has-data-error/card:text-error-light sticky top-(--offset) shrink-0
bg-[color-mix(in_oklab,var(--color-foreground)_calc(var(--progress)*0.25),transparent)]
text-[color-mix(in_oklab,var(--color-background)_var(--progress),var(--color-foreground-muted))]
backdrop-blur-sm
[--progress:min(var(--scroll-y)/50*100%,100%)]
`}
>
Spaces
</h2>
<div className="grid shrink-0 grow items-start">
{(() => {
if (status === 'loading') {
return <Loading className="place-self-center" />;
}
if (error) {
return (
<p data-error className="place-self-center font-semibold">
An error has occurred loading spaces{typeof error === 'string' ? `: ${error}` : ''}
</p>
);
}
if (spaces.length === 0) {
return (
<p data-empty className="text-foreground-muted place-self-center">
No spaces found
</p>
);
}
return (
<ul className="grid-cols-auto-fill-36 grid gap-4">
{spaces.map((space) => (
<li key={space.id} className="group/list-item">
<Popover.Root openOnHover delay={50}>
<Popover.Trigger
className={`
group-nth-[5n]/list-item:bg-gradient-violet
group-nth-[5n+1]/list-item:bg-gradient-lavender
group-nth-[5n+2]/list-item:bg-gradient-aqua
group-nth-[5n+3]/list-item:bg-gradient-peach
group-nth-[5n+4]/list-item:bg-gradient-clearmint
flex aspect-video w-full items-end overflow-clip rounded-lg px-3 py-2
`}
>
<span className="text-sm leading-tight font-semibold">{space.name || space.id}</span>
</Popover.Trigger>
<Popover.Portal>
<Popover.Positioner side="bottom" sideOffset={12}>
<Popover.Popup className="c-popover">
<Popover.Arrow className="c-popover-arrow">
<ArrowSvg />
</Popover.Arrow>
{!('apps' in space) ? (
<Popover.Title className="font-semibold">Public space</Popover.Title>
) : space.apps.length === 0 ? (
<Popover.Title className="font-semibold">
No app has access to this private space
</Popover.Title>
) : (
<>
<Popover.Title className="font-semibold">
Apps with access to this private space
</Popover.Title>
<Popover.Description>
<ul className="list-disc">
{space.apps.map((app) => (
<li key={app.id}>{app.name || app.id}</li>
))}
</ul>
</Popover.Description>
</>
)}
</Popover.Popup>
</Popover.Positioner>
</Popover.Portal>
</Popover.Root>
</li>
))}
</ul>
);
})()}
</div>
</div>
);
}

function ArrowSvg(props: React.ComponentProps<'svg'>) {
return (
<svg width="20" height="10" viewBox="0 0 20 10" role="presentation" {...props}>
<path d="M0 0L20 0L10 10Z" fill="currentColor" />
</svg>
);
}
29 changes: 0 additions & 29 deletions apps/connect/src/components/spaces.tsx

This file was deleted.

Loading
Loading