Skip to content

Commit

Permalink
Merge pull request #64 from su-its/feat/login-cookie
Browse files Browse the repository at this point in the history
feat: Cookieにユーザ情報を保存する
  • Loading branch information
KinjiKawaguchi authored Apr 4, 2024
2 parents ef091c5 + 9f3f2f2 commit 4b33ff1
Show file tree
Hide file tree
Showing 6 changed files with 97 additions and 75 deletions.
7 changes: 3 additions & 4 deletions typing-app/docs/state-management.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,10 @@
- ログインしているかどうか判断したいとき

```tsx
"use client";
import { useUser } from "@/state";
import { getCurrentUser } from "@/app/actions";

export function LoginStatus() {
const user = useUser()
export async function LoginStatus() {
const user = await getCurrentUser();

return <div>User is{user ? " " : " not "}logged in!</div>
}
Expand Down
57 changes: 57 additions & 0 deletions typing-app/src/app/actions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
"use server";
import { client } from "@/libs/api";
import { redirect } from "next/navigation";
import { cookies } from "next/headers";
import { User } from "@/types/user";

type LoginActionState = {
error?: string;
};

export async function login(_: LoginActionState, formData: FormData): Promise<LoginActionState> {
const studentNumber = formData.get("student-number")!.toString();

const { data, error } = await client.GET("/users", {
params: {
query: {
student_number: studentNumber,
},
},
});

if (error) {
console.log(error);
if (/not found/.test(`${error}`.toLowerCase())) {
return { error: "見つかりませんでした" };
}
return { error: "もう一度お試しください" };
}

const expires = new Date(Date.now() + 3 * 60 * 60 * 1000);

const user: User = {
id: data.id!,
handleName: data.handle_name!,
studentNumber: data.student_number!,
};

cookies().set("user", JSON.stringify(user), { expires, httpOnly: true });

redirect("/game");
}

export async function logout() {
cookies().set("user", "", { expires: 0 });
}

export async function getCurrentUser() {
const userStr = cookies().get("user")?.value;
if (!userStr) return null;

function isValidUser(o: any): o is User {
return o && typeof o.id === "string" && typeof o.studentNumber === "string" && typeof o.handleName == "string";
}

const user = JSON.parse(userStr) as User;
return isValidUser(user) ? user : null;
}
19 changes: 6 additions & 13 deletions typing-app/src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import Header from "../components/organism/Header";
import Footer from "../components/organism/Footer";
import "./globals.css";
import { Box, ChakraProvider } from "@chakra-ui/react";
import { LoginProvider } from "@/state";

const inter = Inter({ subsets: ["latin"] });

Expand All @@ -17,23 +16,17 @@ export default async function RootLayout({
}: Readonly<{
children: React.ReactNode;
}>) {
// TODO: 正しくユーザをセットする処理などで置き換える。
// cookie を使えばログイン中は判断できるがログイン/ログアウト時はどうするんだ?
const user = await Promise.resolve({ student_number: "user1", handle_name: "handle1" });

return (
<html lang="en">
<body className={inter.className}>
<ChakraProvider>
<LoginProvider user={user}>
<Box minH="100vh" display="flex" flexDirection="column" bg="black">
<Header />
<Box flex="1" py={2}>
{children}
</Box>
<Footer />
<Box minH="100vh" display="flex" flexDirection="column" bg="black">
<Header />
<Box flex="1" py={2}>
{children}
</Box>
</LoginProvider>
<Footer />
</Box>
</ChakraProvider>
</body>
</html>
Expand Down
55 changes: 26 additions & 29 deletions typing-app/src/components/molecules/LoginModal.tsx
Original file line number Diff line number Diff line change
@@ -1,54 +1,51 @@
import React, { useState } from "react";
import { useRouter } from "next/navigation";
import React from "react";
import {
Modal,
ModalOverlay,
ModalContent,
ModalHeader,
ModalFooter,
ModalBody,
ModalCloseButton,
Button,
Input,
} from "@chakra-ui/react";
import { login } from "@/app/actions";
import { useFormState } from "react-dom";

interface LoginModalProps {
isOpen: boolean;
onClose: () => void;
}

const LoginModal: React.FC<LoginModalProps> = ({ isOpen, onClose }) => {
const [studentId, setStudentId] = useState("");
const router = useRouter();

const handleLogin = async () => {
// TODO:ログイン済みかどうかを判別
// TODO:学籍番号を使用したログイン処理
console.log(studentId);
router.push("/game");
};

const [state, dispatchAction, pending] = useFormState(login, {});
return (
<Modal isOpen={isOpen} onClose={onClose}>
<ModalOverlay />
<ModalContent>
<ModalHeader>続けるにはログインが必要です</ModalHeader>
<ModalBody>
<Input
placeholder="学籍番号を入力してください"
value={studentId}
onChange={(e) => setStudentId(e.target.value)}
/>
</ModalBody>
<form action={dispatchAction}>
<ModalHeader>続けるにはログインが必要です</ModalHeader>
<ModalBody>
<Input
isRequired
required
name="student-number"
placeholder="学籍番号を入力してください"
pattern="[0-9A-Z]{8}"
title="学籍番号"
/>
{state.error ? `エラー: ${state.error}` : null}
</ModalBody>

<ModalFooter>
<Button colorScheme="blue" mr={3} onClick={handleLogin}>
ログインして続行
</Button>
<Button variant="ghost" onClick={onClose}>
閉じる
</Button>
</ModalFooter>
<ModalFooter>
<Button type="submit" colorScheme="blue" mr={3} isLoading={pending}>
ログインして続行
</Button>
<Button variant="ghost" onClick={onClose}>
閉じる
</Button>
</ModalFooter>
</form>
</ModalContent>
</Modal>
);
Expand Down
29 changes: 0 additions & 29 deletions typing-app/src/state/index.tsx

This file was deleted.

5 changes: 5 additions & 0 deletions typing-app/src/types/user.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export type User = {
id: string;
studentNumber: string;
handleName: string;
};

0 comments on commit 4b33ff1

Please sign in to comment.