Skip to content

Commit

Permalink
認証情報をContextに保存して、useIsLoggedIn()などのHookを作成 (#144)
Browse files Browse the repository at this point in the history
* [frontend] Implement AuthProvider and useAuthContext hooks
* [frontend] Add client-auth-provider.tsx
- AuthProvider as client component
- useAuthContext() and useIsLoggedInContext() as client hooks
- Add getCurrentUser() and isLoggedIn() to session.ts (Server-side)
  • Loading branch information
usatie authored Dec 19, 2023
1 parent 1484e52 commit b3cdab4
Show file tree
Hide file tree
Showing 11 changed files with 115 additions and 64 deletions.
18 changes: 5 additions & 13 deletions frontend/app/chat/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,20 @@
import { Separator } from "@/components/ui/separator";
import { testData } from "../test-data";
import { Sidebar } from "../sidebar";
import MessageArea from "../message-area";
import { getUserId } from "@/app/lib/session";
import { getCurrentUserId } from "@/app/lib/session";
import { getUser, getUsers } from "@/app/lib/actions";

//async function getUsers() {
// return testData.users;
//}

export default async function ChatPage({
params: { id },
}: {
params: { id: string };
}) {
const [tmpUsers, currentUserId] = await Promise.all([
const [allUsers, currentUserId] = await Promise.all([
getUsers(),
getUserId(),
getCurrentUserId(),
]);
if (!currentUserId) {
throw new Error("getUserId error");
}
const currentUser = await getUser(parseInt(currentUserId));
const users = tmpUsers.filter((user) => user.id !== parseInt(currentUserId));
const currentUser = await getUser(currentUserId);
const users = allUsers.filter((user) => user.id !== currentUserId);
if (users.length === 0) {
console.error("No other users exist");
throw new Error("No other users exist");
Expand Down
17 changes: 4 additions & 13 deletions frontend/app/chat/page.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,14 @@
import { Separator } from "@/components/ui/separator";
//import { testData } from "./test-data";
import { Sidebar } from "./sidebar";
import MessageArea from "./message-area";
import { getUserId } from "@/app/lib/session";
import { getUsers } from "@/app/lib/actions";

//async function getUsers() {
// return testData.users;
//}
import { getCurrentUserId } from "@/app/lib/session";

export default async function ChatPage() {
const [tmpUsers, currentUserId] = await Promise.all([
const [allUsers, currentUserId] = await Promise.all([
getUsers(),
getUserId(),
getCurrentUserId(),
]);
if (!currentUserId) {
throw new Error("getUserId error");
}
const users = tmpUsers.filter((user) => user.id !== parseInt(currentUserId));
const users = allUsers.filter((user) => user.id !== currentUserId);
return (
<>
<div className="overflow-auto flex-grow flex gap-4 pb-4">
Expand Down
26 changes: 26 additions & 0 deletions frontend/app/client-auth-provider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
"use client";

import { AuthContext } from "@/app/lib/client-auth";

type User = {
id: number;
name: string;
email: string;
avatarURL?: string;
createdAt: string;
};

export type AuthProviderProps = {
user?: User;
children: React.ReactNode;
isLoggedIn: boolean;
};

export default function AuthProvider({
children,
user,
isLoggedIn,
}: AuthProviderProps) {
const auth = { currentUser: user, isLoggedIn };
return <AuthContext.Provider value={auth}>{children}</AuthContext.Provider>;
}
20 changes: 14 additions & 6 deletions frontend/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";

import AuthProvider from "./client-auth-provider";
import { getCurrentUser, isLoggedIn } from "@/app/lib/session";

// components
import { ThemeProvider } from "@/components/theme-provider";
import { Toaster } from "@/components/ui/toaster";
Expand All @@ -16,11 +19,14 @@ export const metadata: Metadata = {
description: "Classic Pong game",
};

export default function RootLayout({
export default async function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
const isAuthorized = await isLoggedIn(); // Not allow expired tokens
const user = await getCurrentUser(); // Allows expired tokens

return (
<html lang="en" suppressHydrationWarning>
<body className={"overflow-hidden " + inter.className}>
Expand All @@ -30,11 +36,13 @@ export default function RootLayout({
enableSystem
disableTransitionOnChange
>
<div className="flex flex-col px-16 h-[100vh]">
<Nav />
{children}
</div>
<Toaster />
<AuthProvider user={user} isLoggedIn={isAuthorized}>
<div className="flex flex-col px-16 h-[100vh]">
<Nav />
{children}
</div>
<Toaster />
</AuthProvider>
</ThemeProvider>
</body>
</html>
Expand Down
7 changes: 7 additions & 0 deletions frontend/app/lib/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -249,3 +249,10 @@ export async function deleteRoomUser(roomId: number, userId: number) {
return "Success";
}
}

export async function signInAsTestUser() {
const email = "[email protected]";
const password = "password-test";
await signIn({ email, password });
redirect("/");
}
21 changes: 21 additions & 0 deletions frontend/app/lib/client-auth.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
"use client";
import { createContext, useContext } from "react";

type User = {
id: number;
name: string;
email: string;
avatarURL?: string;
createdAt: string;
};

type AuthContextType = {
currentUser?: User;
isLoggedIn: boolean;
};

export const AuthContext = createContext<AuthContextType>({
isLoggedIn: false,
});

export const useAuthContext = () => useContext(AuthContext);
27 changes: 24 additions & 3 deletions frontend/app/lib/session.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { cookies } from "next/headers";
import * as jose from "jose";
import { getUser } from "./actions";

// TODO: add types
export type Session = {};
Expand All @@ -22,10 +23,30 @@ export async function isLoggedIn() {
return true;
}

export async function getUserId(): Promise<string | null> {
export async function getCurrentUser(): Promise<any> {
try {
const userId = await getCurrentUserId();
const user = getUser(userId);
return user;
} catch (e) {
console.log("getCurrentUser: ", e);
return null;
}
}

// Only use this function if you are sure that the user is logged in
export async function getCurrentUserId(): Promise<number> {
const payload = await getAccessTokenPayload({ ignoreExpiration: true });
if (!payload) return null;
return payload.userId as string;
const userId = payload?.userId?.toString();
// If userId is not set, then return null
if (!userId) {
throw new Error("userId is not set");
}
// If invalid userId, then return null
if (!userId.match(/^[0-9]+$/)) {
throw new Error("invalid userId");
}
return parseInt(userId);
}

async function getAccessTokenPayload(options: any) {
Expand Down
13 changes: 4 additions & 9 deletions frontend/app/room/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
// );
//}

import { getUserId } from "@/app/lib/session";
import { getCurrentUserId } from "@/app/lib/session";
import { Separator } from "@/components/ui/separator";
import { Sidebar } from "../sidebar";
import { getRoom, getUsers, getUser } from "@/app/lib/actions";
Expand All @@ -44,11 +44,7 @@ export default async function Page({
}: {
params: { id: number };
}) {
const currentUserId = await getUserId();
if (!currentUserId) {
console.error("getUserId error");
throw new Error("getUserId error");
}
const currentUserId = await getCurrentUserId();
const roomInfo = await getRoom(id);
if (!roomInfo) {
notFound();
Expand All @@ -60,10 +56,9 @@ export default async function Page({
.sort((a: UserRole, b: UserRole) => a.id - b.id);

const myInfo = roomInfo.users.find((user: UserOnRoom) => {
return user.userId === parseInt(currentUserId);
return user.userId === currentUserId;
});
if (!myInfo) {
console.error("Not participating in room");
throw new Error("Not participating in room");
}
const allUsers = await getUsers();
Expand All @@ -76,7 +71,7 @@ export default async function Page({
role: usersRole[i].role,
}),
);
const currentUser = await getUser(parseInt(currentUserId));
const currentUser = await getUser(currentUserId);
return (
<>
<div className="overflow-auto flex-grow flex gap-4 pb-4">
Expand Down
10 changes: 3 additions & 7 deletions frontend/app/ui/direct-message/dm-sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card";
import { ScrollArea } from "@/components/ui/scroll-area";
import { Separator } from "@/components/ui/separator";
import { getUsers } from "@/app/lib/actions";
import { getUserId } from "@/app/lib/session";
import { getCurrentUserId } from "@/app/lib/session";
import { UserButton } from "@/app/ui/direct-message/user-button";

const DirectMessageSidebar = async () => {
Expand All @@ -11,12 +11,8 @@ const DirectMessageSidebar = async () => {
console.error("getUsers Error");
return null;
}
const currentUserId = await getUserId();
if (!currentUserId) {
console.error("getUserId Error");
return null;
}
const users = tmpUsers.filter((user) => parseInt(currentUserId) !== user.id);
const currentUserId = await getCurrentUserId();
const users = tmpUsers.filter((user) => currentUserId !== user.id);
return (
<div>
<Card className="flex flex-col h-full text-primary w-full dark:bg-[#1E1F22] bg-[#F2F3F5]">
Expand Down
11 changes: 1 addition & 10 deletions frontend/app/ui/nav.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { ModeToggle } from "@/components/toggle-mode";
import { Button } from "@/components/ui/button";
import Link from "next/link";
import { signIn, signOut } from "@/app/lib/actions";
import { signInAsTestUser, signOut } from "@/app/lib/actions";
import { isLoggedIn } from "@/app/lib/session";
import { redirect } from "next/navigation";

function AuthorizedMenu() {
return (
Expand All @@ -20,14 +19,6 @@ function AuthorizedMenu() {
);
}

async function signInAsTestUser() {
"use server";
const email = "[email protected]";
const password = "password-test";
await signIn({ email, password });
redirect("/");
}

function UnauthorizedMenu() {
return (
<li className="flex gap-8 items-center">
Expand Down
9 changes: 6 additions & 3 deletions frontend/app/ui/profile/profile-form.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
"use client";
import { Skeleton } from "@/components/ui/skeleton";
import { Separator } from "@/components/ui/separator";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Stack } from "@/app/ui/layout/stack";
import { Label } from "@/components/ui/label";
import { useAuthContext } from "@/app/lib/client-auth";

function AvatarSkeleton() {
return <Skeleton className="rounded-full h-20 w-20" />;
Expand All @@ -23,10 +25,11 @@ function ProfileItem({ type, title, value }: ProfileItemProps) {
export type ProfileItemProps = {
type: string;
title: string;
value: string;
value?: string;
};

export default function ProfileForm() {
const { currentUser } = useAuthContext();
// Menu: min 100px
// Profile : the rest
return (
Expand All @@ -37,8 +40,8 @@ export default function ProfileForm() {
<Separator />
</Stack>
<AvatarSkeleton />
<ProfileItem type="text" title="name" value="susami" />
<ProfileItem type="email" title="email" value="[email protected]" />
<ProfileItem type="text" title="name" value={currentUser?.name} />
<ProfileItem type="email" title="email" value={currentUser?.email} />
<div></div>
<Button variant="outline" className="w-40">
Save
Expand Down

0 comments on commit b3cdab4

Please sign in to comment.