Skip to content

Commit 9e2f14a

Browse files
authored
add connect app scaffold (#189)
1 parent 6d7831c commit 9e2f14a

26 files changed

+928
-1
lines changed

apps/connect/components.json

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"$schema": "https://ui.shadcn.com/schema.json",
3+
"style": "new-york",
4+
"rsc": false,
5+
"tsx": true,
6+
"tailwind": {
7+
"config": "tailwind.config.js",
8+
"css": "src/index.css",
9+
"baseColor": "neutral",
10+
"cssVariables": true,
11+
"prefix": ""
12+
},
13+
"aliases": {
14+
"components": "@/components",
15+
"utils": "@/lib/utils",
16+
"ui": "@/components/ui",
17+
"lib": "@/lib",
18+
"hooks": "@/hooks"
19+
}
20+
}

apps/connect/index.html

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
7+
<title>Connect</title>
8+
<meta name="description" content="Authenticate with your Geo Account" />
9+
</head>
10+
<body>
11+
<div id="root"></div>
12+
<script type="module" src="/src/main.tsx"></script>
13+
</body>
14+
</html>

apps/connect/package.json

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
{
2+
"name": "events",
3+
"private": true,
4+
"version": "0.0.0",
5+
"type": "module",
6+
"scripts": {
7+
"dev": "vite --force",
8+
"preview": "vite preview"
9+
},
10+
"dependencies": {
11+
"@graphprotocol/grc-20": "^0.11.5",
12+
"@privy-io/react-auth": "^2.13.0",
13+
"@radix-ui/react-avatar": "^1.1.9",
14+
"@radix-ui/react-icons": "^1.3.2",
15+
"@radix-ui/react-slot": "^1.2.2",
16+
"@tanstack/react-query": "^5.75.5",
17+
"@tanstack/react-router": "^1.120.2",
18+
"class-variance-authority": "^0.7.1",
19+
"clsx": "^2.1.1",
20+
"effect": "^3.14.20",
21+
"framer-motion": "^12.10.1",
22+
"lucide-react": "^0.508.0",
23+
"react": "^19.1.0",
24+
"react-dom": "^19.1.0",
25+
"tailwind-merge": "^3.2.0",
26+
"tailwindcss-animate": "^1.0.7",
27+
"vite": "^6.3.5"
28+
},
29+
"devDependencies": {
30+
"@tailwindcss/vite": "^4.1.5",
31+
"@tanstack/router-devtools": "^1.120.2",
32+
"@tanstack/router-plugin": "^1.120.2",
33+
"@types/node": "^22.15.15",
34+
"@types/react": "^19.1.3",
35+
"@types/react-dom": "^19.1.3",
36+
"@vitejs/plugin-react": "^4.4.1",
37+
"tailwindcss": "^4.1.5"
38+
}
39+
}

apps/connect/public/favicon.svg

Lines changed: 3 additions & 0 deletions
Loading

apps/connect/src/Boot.tsx

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { PrivyProvider } from '@privy-io/react-auth';
2+
import { RouterProvider, createRouter } from '@tanstack/react-router';
3+
import { routeTree } from './routeTree.gen';
4+
5+
// Create a new router instance
6+
const router = createRouter({ routeTree });
7+
8+
// Register the router instance for type safety
9+
declare module '@tanstack/react-router' {
10+
interface Register {
11+
router: typeof router;
12+
}
13+
}
14+
15+
export function Boot() {
16+
return (
17+
<PrivyProvider
18+
appId="cm4wx6ziv00ngrmfjf9ik36iu"
19+
config={{
20+
loginMethods: ['email', 'wallet', 'google', 'twitter', 'github'],
21+
appearance: {
22+
theme: 'light',
23+
accentColor: '#676FFF',
24+
},
25+
embeddedWallets: {
26+
createOnLogin: 'users-without-wallets',
27+
},
28+
}}
29+
>
30+
<RouterProvider router={router} />
31+
</PrivyProvider>
32+
);
33+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { usePrivy } from '@privy-io/react-auth';
2+
import { useRouter } from '@tanstack/react-router';
3+
import { Loader2 } from 'lucide-react';
4+
import { useState } from 'react';
5+
import { Button } from './ui/button';
6+
7+
export function Logout() {
8+
const { logout: privyLogout, ready, authenticated } = usePrivy();
9+
const router = useRouter();
10+
const [isLoading, setIsLoading] = useState(false);
11+
const disconnectWallet = async () => {
12+
setIsLoading(true);
13+
await privyLogout();
14+
router.navigate({
15+
to: '/login',
16+
});
17+
setIsLoading(false);
18+
};
19+
20+
return (
21+
<Button className="home-button" onClick={() => disconnectWallet()} disabled={!ready || !authenticated}>
22+
{isLoading ? (
23+
<>
24+
<Loader2 className="w-4 h-4 animate-spin" /> Logout
25+
</>
26+
) : (
27+
'Logout'
28+
)}
29+
</Button>
30+
);
31+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
'use client';
2+
3+
import { cn } from '@/lib/utils';
4+
import { motion } from 'framer-motion';
5+
6+
interface SpinnerProps {
7+
size?: 'sm' | 'md' | 'lg' | 'xl';
8+
color?: 'default' | 'primary' | 'secondary' | 'accent' | 'white';
9+
className?: string;
10+
}
11+
12+
export function Spinner({ size = 'md', color = 'primary', className }: SpinnerProps) {
13+
const sizeClasses = {
14+
sm: 'h-4 w-4 border-2',
15+
md: 'h-6 w-6 border-2',
16+
lg: 'h-8 w-8 border-3',
17+
xl: 'h-12 w-12 border-4',
18+
};
19+
20+
const colorClasses = {
21+
default: 'border-muted-foreground/30 border-t-muted-foreground',
22+
primary: 'border-primary/30 border-t-primary',
23+
secondary: 'border-secondary/30 border-t-secondary',
24+
accent: 'border-accent/30 border-t-accent',
25+
white: 'border-white/30 border-t-white',
26+
};
27+
28+
return (
29+
<motion.div
30+
className={cn('rounded-full animate-spin', sizeClasses[size], colorClasses[color], className)}
31+
animate={{ rotate: 360 }}
32+
transition={{
33+
duration: 1,
34+
ease: 'linear',
35+
repeat: Number.POSITIVE_INFINITY,
36+
}}
37+
aria-label="Loading"
38+
>
39+
<span className="sr-only">Loading...</span>
40+
</motion.div>
41+
);
42+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import * as AvatarPrimitive from '@radix-ui/react-avatar';
2+
import * as React from 'react';
3+
4+
import { cn } from '@/lib/utils';
5+
6+
const Avatar = React.forwardRef<
7+
React.ElementRef<typeof AvatarPrimitive.Root>,
8+
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>
9+
>(({ className, ...props }, ref) => (
10+
<AvatarPrimitive.Root
11+
ref={ref}
12+
className={cn('relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full', className)}
13+
{...props}
14+
/>
15+
));
16+
Avatar.displayName = AvatarPrimitive.Root.displayName;
17+
18+
const AvatarImage = React.forwardRef<
19+
React.ElementRef<typeof AvatarPrimitive.Image>,
20+
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>
21+
>(({ className, ...props }, ref) => (
22+
<AvatarPrimitive.Image ref={ref} className={cn('aspect-square h-full w-full', className)} {...props} />
23+
));
24+
AvatarImage.displayName = AvatarPrimitive.Image.displayName;
25+
26+
const AvatarFallback = React.forwardRef<
27+
React.ElementRef<typeof AvatarPrimitive.Fallback>,
28+
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>
29+
>(({ className, ...props }, ref) => (
30+
<AvatarPrimitive.Fallback
31+
ref={ref}
32+
className={cn('flex h-full w-full items-center justify-center rounded-full bg-muted', className)}
33+
{...props}
34+
/>
35+
));
36+
AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName;
37+
38+
export { Avatar, AvatarImage, AvatarFallback };
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { Slot } from '@radix-ui/react-slot';
2+
import { type VariantProps, cva } from 'class-variance-authority';
3+
import * as React from 'react';
4+
5+
import { cn } from '@/lib/utils';
6+
7+
const buttonVariants = cva(
8+
'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50',
9+
{
10+
variants: {
11+
variant: {
12+
default: 'bg-primary text-primary-foreground shadow-sm hover:bg-primary/90',
13+
destructive: 'bg-destructive text-destructive-foreground shadow-xs hover:bg-destructive/90',
14+
outline: 'border border-input bg-background shadow-xs hover:bg-accent hover:text-accent-foreground',
15+
secondary: 'bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80',
16+
ghost: 'hover:bg-accent hover:text-accent-foreground',
17+
link: 'text-primary underline-offset-4 hover:underline',
18+
},
19+
size: {
20+
default: 'h-9 px-4 py-2',
21+
sm: 'h-8 rounded-md px-3 text-xs',
22+
lg: 'h-10 rounded-md px-8',
23+
icon: 'h-9 w-9',
24+
},
25+
},
26+
defaultVariants: {
27+
variant: 'default',
28+
size: 'default',
29+
},
30+
},
31+
);
32+
33+
export interface ButtonProps
34+
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
35+
VariantProps<typeof buttonVariants> {
36+
asChild?: boolean;
37+
}
38+
39+
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
40+
({ className, variant, size, asChild = false, ...props }, ref) => {
41+
const Comp = asChild ? Slot : 'button';
42+
return <Comp className={cn(buttonVariants({ variant, size, className }))} ref={ref} {...props} />;
43+
},
44+
);
45+
Button.displayName = 'Button';
46+
47+
// eslint-disable-next-line react-refresh/only-export-components
48+
export { Button, buttonVariants };
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import * as React from 'react';
2+
3+
import { cn } from '@/lib/utils';
4+
5+
const Card = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(({ className, ...props }, ref) => (
6+
<div ref={ref} className={cn('rounded-xl border bg-card text-card-foreground shadow-sm', className)} {...props} />
7+
));
8+
Card.displayName = 'Card';
9+
10+
const CardHeader = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
11+
({ className, ...props }, ref) => (
12+
<div ref={ref} className={cn('flex flex-col space-y-1.5 p-6', className)} {...props} />
13+
),
14+
);
15+
CardHeader.displayName = 'CardHeader';
16+
17+
const CardTitle = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLHeadingElement>>(
18+
({ className, ...props }, ref) => (
19+
<h3 ref={ref} className={cn('font-semibold leading-none tracking-tight', className)} {...props} />
20+
),
21+
);
22+
CardTitle.displayName = 'CardTitle';
23+
24+
const CardDescription = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLParagraphElement>>(
25+
({ className, ...props }, ref) => (
26+
<p ref={ref} className={cn('text-sm text-muted-foreground', className)} {...props} />
27+
),
28+
);
29+
CardDescription.displayName = 'CardDescription';
30+
31+
const CardContent = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
32+
({ className, ...props }, ref) => <div ref={ref} className={cn('p-6 pt-0', className)} {...props} />,
33+
);
34+
CardContent.displayName = 'CardContent';
35+
36+
const CardFooter = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
37+
({ className, ...props }, ref) => (
38+
<div ref={ref} className={cn('flex items-center p-6 pt-0', className)} {...props} />
39+
),
40+
);
41+
CardFooter.displayName = 'CardFooter';
42+
43+
export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent };

0 commit comments

Comments
 (0)