Skip to content

Commit

Permalink
feat: Auth with wrapper and Privy working
Browse files Browse the repository at this point in the history
  • Loading branch information
RezaRahemtola committed Oct 20, 2024
1 parent a13c7f3 commit 34d967a
Show file tree
Hide file tree
Showing 9 changed files with 301 additions and 122 deletions.
6 changes: 6 additions & 0 deletions front/.eslintrc.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,10 @@ rules:
no-plusplus: [ "error", { "allowForLoopAfterthoughts": true } ]
no-console: [ "warn", { allow: [ "error" ] } ]

"@typescript-eslint/no-unused-vars":
- warn
- argsIgnorePattern: "^_"
varsIgnorePattern: "^_"
args: "all"

perfectionist/sort-imports: [ "warn", { internalPattern: [ '@/**' ], } ]
102 changes: 102 additions & 0 deletions front/src/app/(auth)/auth/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
"use client";

import { useLogin, useModalStatus } from "@privy-io/react-auth";
import { useRouter } from "next/navigation";
import { useEffect, useRef } from "react";
import { useAccount } from "wagmi";

export default function Component() {
const canvasRef = useRef<HTMLCanvasElement>(null);
const router = useRouter();
const account = useAccount();

const { login } = useLogin({ onComplete: () => router.push("/") });
const { isOpen: isPrivyModalOpen } = useModalStatus();

useEffect(() => {
if (!isPrivyModalOpen) {
login();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isPrivyModalOpen]);

useEffect(() => {
if (account.isConnected) {
router.push("/");
return;
}

const canvas = canvasRef.current;
if (!canvas) return;

const ctx = canvas.getContext("2d");
if (!ctx) return;

document.querySelector("html")?.removeAttribute("onclick");

const resizeCanvas = () => {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
};

resizeCanvas();
window.addEventListener("resize", resizeCanvas);

const dots: { x: number; y: number; vx: number; vy: number }[] = [];
const dotCount = 100;
const dotSize = 2;
const connectionDistance = 100;

for (let i = 0; i < dotCount; i++) {
dots.push({
x: Math.random() * canvas.width,
y: Math.random() * canvas.height,
vx: (Math.random() - 0.5) * 0.5,
vy: (Math.random() - 0.5) * 0.5,
});
}
document.querySelector("#privy-dialog-backdrop")?.removeAttribute("onclick");

const animate = () => {
ctx.clearRect(0, 0, canvas.width, canvas.height);

dots.forEach((dot) => {
dot.x += dot.vx;
dot.y += dot.vy;

if (dot.x < 0 || dot.x > canvas.width) dot.vx *= -1;
if (dot.y < 0 || dot.y > canvas.height) dot.vy *= -1;

ctx.beginPath();
ctx.arc(dot.x, dot.y, dotSize, 0, Math.PI * 2);
ctx.fillStyle = "rgba(255, 255, 255, 0.5)";
ctx.fill();

dots.forEach((otherDot) => {
const distance = Math.hypot(dot.x - otherDot.x, dot.y - otherDot.y);
if (distance < connectionDistance) {
ctx.beginPath();
ctx.moveTo(dot.x, dot.y);
ctx.lineTo(otherDot.x, otherDot.y);
ctx.strokeStyle = `rgba(255, 255, 255, ${0.2 - (distance / connectionDistance) * 0.2})`;
ctx.stroke();
}
});
});

requestAnimationFrame(animate);
};

animate();

return () => {
window.removeEventListener("resize", resizeCanvas);
};
}, []);

return (
<div className="fixed inset-0 bg-gradient-to-br from-blue-900 to-indigo-900">
<canvas ref={canvasRef} className="absolute inset-0" style={{ mixBlendMode: "screen" }} />
</div>
);
}
12 changes: 12 additions & 0 deletions front/src/app/(drive)/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { ReactNode } from "react";

import { AuthWrapper } from "@/components/AuthWrapper";
import { BedrockSidebar } from "@/components/BedrockSidebar";

export default function DriveLayout({ children }: Readonly<{ children: ReactNode }>) {
return (
<AuthWrapper>
<BedrockSidebar>{children}</BedrockSidebar>
</AuthWrapper>
);
}
File renamed without changes.
13 changes: 5 additions & 8 deletions front/src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@ import { type Metadata } from "next";
import localFont from "next/font/local";
import { ReactNode } from "react";

import { Providers } from "@/app/providers";

import "./globals.css";
import { BedrockSidebar } from "@/components/BedrockSidebar";

import { Providers } from "@/app/providers";

const geistSans = localFont({
src: "./fonts/GeistVF.woff",
Expand All @@ -19,8 +18,8 @@ const geistMono = localFont({
});

export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
title: "Bedrock",
description: "TODO",
};

export default function RootLayout({
Expand All @@ -31,9 +30,7 @@ export default function RootLayout({
return (
<html lang="en">
<body className={`${geistSans.variable} ${geistMono.variable} antialiased`}>
<Providers>
<BedrockSidebar>{children}</BedrockSidebar>
</Providers>
<Providers>{children}</Providers>
</body>
</html>
);
Expand Down
47 changes: 47 additions & 0 deletions front/src/components/AuthWrapper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
"use client";

import { LoaderIcon } from "lucide-react";
import { useRouter } from "next/navigation";
import { ReactNode, useCallback, useEffect, useRef, useState } from "react";
import { useAccount } from "wagmi";

type AuthWrapperProps = {
children: ReactNode;
};

export const AuthWrapper = ({ children }: AuthWrapperProps) => {
const [_authChecked, setAuthChecked] = useState(false);
const [currentAuthStatus, setCurrentAuthStatus] = useState<boolean | undefined>(undefined);
const latestIsConnected = useRef<boolean>();

const account = useAccount();
const router = useRouter();

const checkAuthStatus = useCallback(() => {
if (latestIsConnected.current === undefined) {
return;
}

if (!latestIsConnected.current) {
router.push("/auth");
}
latestIsConnected.current = undefined;
setAuthChecked(true);
}, [latestIsConnected, router]);

useEffect(() => {
setCurrentAuthStatus(account.isConnected);
if (latestIsConnected.current === undefined) {
latestIsConnected.current = account.isConnected;
setTimeout(() => {
checkAuthStatus();
}, 2000);
}
}, [account, checkAuthStatus, currentAuthStatus]);

if (currentAuthStatus) {
return <>{children}</>;
} else {
return <LoaderIcon className="animate-spin m-auto h-[100vh]" />;
}
};
101 changes: 101 additions & 0 deletions front/src/components/BedrockAccountMenu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
"use client";

import { useLogout } from "@privy-io/react-auth";
import { BadgeCheck, Bell, ChevronsUpDown, CreditCard, LogOut, Sparkles } from "lucide-react";
import { useDisconnect } from "wagmi";

import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { SidebarMenuButton } from "@/components/ui/sidebar";

const user = {
name: "shadcn",
email: "[email protected]",
// TODO: find an avatar image
avatar: "/avatars/shadcn.jpg",
};

export const BedrockAccountMenu = () => {
const { disconnect } = useDisconnect();
const { logout } = useLogout();

return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<SidebarMenuButton
size="lg"
className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground"
>
<Avatar className="h-8 w-8 rounded-lg">
<AvatarImage src={user.avatar} alt={user.name} />
<AvatarFallback className="rounded-lg">CN</AvatarFallback>
</Avatar>
<div className="grid flex-1 text-left text-sm leading-tight">
<span className="truncate font-semibold">{user.name}</span>
<span className="truncate text-xs">{user.email}</span>
</div>
<ChevronsUpDown className="ml-auto size-4" />
</SidebarMenuButton>
</DropdownMenuTrigger>
<DropdownMenuContent
className="w-[--radix-dropdown-menu-trigger-width] min-w-56 rounded-lg"
side="bottom"
align="end"
sideOffset={4}
>
<DropdownMenuLabel className="p-0 font-normal">
<div className="flex items-center gap-2 px-1 py-1.5 text-left text-sm">
<Avatar className="h-8 w-8 rounded-lg">
<AvatarImage src={user.avatar} alt={user.name} />
<AvatarFallback className="rounded-lg">CN</AvatarFallback>
</Avatar>
<div className="grid flex-1 text-left text-sm leading-tight">
<span className="truncate font-semibold">{user.name}</span>
<span className="truncate text-xs">{user.email}</span>
</div>
</div>
</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuGroup>
<DropdownMenuItem>
<Sparkles />
Upgrade to Pro
</DropdownMenuItem>
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuGroup>
<DropdownMenuItem>
<BadgeCheck />
Account
</DropdownMenuItem>
<DropdownMenuItem>
<CreditCard />
Billing
</DropdownMenuItem>
<DropdownMenuItem>
<Bell />
Notifications
</DropdownMenuItem>
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuItem
onClick={() => {
disconnect();
logout();
}}
>
<LogOut />
Log out
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
};
Loading

0 comments on commit 34d967a

Please sign in to comment.