Skip to content

Commit

Permalink
フロントエンドのgoalとpostを結合
Browse files Browse the repository at this point in the history
  • Loading branch information
MurakawaTakuya committed Dec 20, 2024
1 parent df127d4 commit dccc6c2
Show file tree
Hide file tree
Showing 8 changed files with 85 additions and 101 deletions.
14 changes: 10 additions & 4 deletions src/Components/DashBoard/DashBoard.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use client";
import { showSnackBar } from "@/Components/SnackBar/SnackBar";
import { GoalWithId, SuccessResult } from "@/types/types";
import { GoalWithIdAndUserData } from "@/types/types";
import {
fetchResult,
handleFetchResultError,
Expand All @@ -25,9 +25,15 @@ export default function DashBoard({
pending?: boolean;
orderBy?: "asc" | "desc";
} = {}) {
const [successResults, setSuccessResults] = useState<SuccessResult[]>([]);
const [failedResults, setFailedResults] = useState<GoalWithId[]>([]);
const [pendingResults, setPendingResults] = useState<GoalWithId[]>([]);
const [successResults, setSuccessResults] = useState<GoalWithIdAndUserData[]>(
[]
);
const [failedResults, setFailedResults] = useState<GoalWithIdAndUserData[]>(
[]
);
const [pendingResults, setPendingResults] = useState<GoalWithIdAndUserData[]>(
[]
);
const [noResult, setNoResult] = useState<boolean>(false);
const [isLoading, setIsLoading] = useState<boolean>(true);

Expand Down
6 changes: 3 additions & 3 deletions src/Components/DeletePostModal/DeletePostModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,16 @@ import { useState } from "react";
import { showSnackBar } from "../SnackBar/SnackBar";

export default function DeletePostModal({
postId,
goalId,
deadline,
}: {
postId: string;
goalId: string;
deadline: string;
}) {
const [open, setOpen] = useState(false);

const handleDeletePost = async () => {
const response = await fetch(`${functionsEndpoint}/post/${postId}`, {
const response = await fetch(`${functionsEndpoint}/post/${goalId}`, {
method: "DELETE",
headers: {
"X-Firebase-AppCheck": appCheckToken,
Expand Down
6 changes: 3 additions & 3 deletions src/Components/PostModal/PostModal.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use client";
import { showSnackBar } from "@/Components/SnackBar/SnackBar";
import { Post } from "@/types/types";
import { PostWithGoalId } from "@/types/types";
import { createPost, handleCreatePostError } from "@/utils/API/Post/createPost";
import { uploadImage } from "@/utils/Uploader";
import { useUser } from "@/utils/UserContext";
Expand Down Expand Up @@ -86,9 +86,9 @@ export default function PostModal({ goalId }: { goalId: string }) {
image,
(percent) => setProgress(percent),
async (url) => {
const postData: Post = {
const postData: PostWithGoalId = {
userId: user?.userId as string,
storedId: url,
storedURL: url,
text: text,
goalId: goalId,
submittedAt: new Date(),
Expand Down
104 changes: 45 additions & 59 deletions src/Components/Progress/Progress.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { GoalWithId, SuccessResult, UserData } from "@/types/types";
import { fetchUserById } from "@/utils/API/User/fetchUser";
import { GoalWithIdAndUserData, User } from "@/types/types";
import { formatStringToDate } from "@/utils/DateFormatter";
import { useUser } from "@/utils/UserContext";
import AppRegistrationRoundedIcon from "@mui/icons-material/AppRegistrationRounded";
Expand All @@ -12,7 +11,7 @@ import StepIndicator, { stepIndicatorClasses } from "@mui/joy/StepIndicator";
import Stepper from "@mui/joy/Stepper";
import Typography, { typographyClasses } from "@mui/joy/Typography";
import { Divider } from "@mui/material";
import { ReactNode, useEffect, useState } from "react";
import { ReactNode } from "react";
import DeleteGoalModal from "../DeleteGoalModal/DeleteGoalModal";
import DeletePostModal from "../DeletePostModal/DeletePostModal";
import PostModal from "../PostModal/PostModal";
Expand All @@ -36,9 +35,9 @@ const innerBorderColors = {
};

interface ProgressProps {
successResults?: SuccessResult[];
failedResults?: GoalWithId[];
pendingResults?: GoalWithId[];
successResults?: GoalWithIdAndUserData[];
failedResults?: GoalWithIdAndUserData[];
pendingResults?: GoalWithIdAndUserData[];
orderBy?: "asc" | "desc";
}

Expand All @@ -48,43 +47,23 @@ export default function Progress({
pendingResults = [],
orderBy = "desc", // 最新が上位
}: ProgressProps) {
const [userNames, setUserNames] = useState<Record<string, string>>({}); // <userId, userName>
const { user } = useUser();

const fetchUserName = async (userId: string) => {
if (userNames[userId]) return; // 既に取得済みの場合はキャッシュのように再利用
setUserNames((prev) => ({ ...prev, [userId]: "Loading..." }));
try {
const userData = await fetchUserById(userId);
setUserNames((prev) => ({ ...prev, [userId]: userData.name }));
} catch (error) {
console.error("Failed to fetch user name:", error);
setUserNames((prev) => ({ ...prev, [userId]: "Unknown user" }));
}
};

useEffect(() => {
const allUserIds = [
...successResults.map((result) => result.userId),
...failedResults.map((result) => result.userId),
...pendingResults.map((result) => result.userId),
];
// 同じuserIdに対して1回だけ取得し、キャッシュする
const uniqueUserIds = Array.from(new Set(allUserIds));
uniqueUserIds.forEach((userId) => fetchUserName(userId));
}, [successResults, failedResults, pendingResults, fetchUserName]);

const allResults = [
...successResults.map((result) => ({ ...result, type: "success" })),
...failedResults.map((result) => ({ ...result, type: "failed" })),
...pendingResults.map((result) => ({ ...result, type: "pending" })),
];

console.log("allResults: ", allResults);

// typeがsuccessの場合はsubmittedAtでソートし、それ以外の場合はdeadlineでソートする
allResults.sort((a, b) => {
const getUpdatedTime = (item: typeof a) => {
if (item.type === "success" && "submittedAt" in item) {
return new Date(item.submittedAt).getTime();
if (item.type === "success" && "post" in item) {
return item.post?.submittedAt
? new Date(item.post.submittedAt).getTime()
: 0;
}
return new Date(item.deadline).getTime();
};
Expand All @@ -96,33 +75,32 @@ export default function Progress({
return (
<>
{allResults.map((result) => {
const userName = userNames[result.userId] || "Loading...";
if (result.type === "success") {
return successStep(
result as SuccessResult,
userName,
user as UserData
);
return successStep(result as GoalWithIdAndUserData, user as User);
}
if (result.type === "failed") {
return failedStep(result as GoalWithId, userName, user as UserData);
return failedStep(result as GoalWithIdAndUserData, user as User);
}
if (result.type === "pending") {
return pendingStep(result as GoalWithId, userName, user as UserData);
return pendingStep(result as GoalWithIdAndUserData, user as User);
}
return null;
})}
</>
);
}

const successStep = (
result: SuccessResult,
userName: string,
user: UserData
) => {
const successStep = (result: GoalWithIdAndUserData, user: User) => {
const post = result.post;
if (!post) {
return null;
}
return (
<StepperBlock key={result.goalId} userName={userName} resultType="success">
<StepperBlock
key={result.goalId}
userName={result.userData.name}
resultType="success"
>
<Step
active
completed
Expand All @@ -134,7 +112,7 @@ const successStep = (
>
<GoalCard
deadline={result.deadline}
goalText={result.goalText}
goalText={result.text}
resultType="success"
goalId={result.goalId}
userId={result.userId}
Expand All @@ -161,10 +139,10 @@ const successStep = (
zIndex: 0,
}}
>
{result.storedId && (
{post.storedURL && (
<img
src={result.storedId}
srcSet={result.storedId}
src={post.storedURL}
srcSet={post.storedURL}
style={{
objectFit: "contain",
maxWidth: "100%",
Expand All @@ -180,20 +158,20 @@ const successStep = (
>
<div style={{ display: "flex", justifyContent: "space-between" }}>
<Typography level="body-sm">
{formatStringToDate(result.submittedAt)}に完了
{formatStringToDate(post.submittedAt)}に完了
</Typography>
{/* 自分の作成した投稿のみ削除できるようにする */}
{result.userId === user?.userId && (
<DeletePostModal
postId={result.postId}
goalId={result.goalId}
deadline={result.deadline}
/>
)}
</div>
{result.postText && (
{post.text && (
<>
<Divider />
<Typography level="title-md">{result.postText}</Typography>
<Typography level="title-md">{post.text}</Typography>
</>
)}
</CardContent>
Expand All @@ -203,9 +181,13 @@ const successStep = (
);
};

const failedStep = (result: GoalWithId, userName: string, user: UserData) => {
const failedStep = (result: GoalWithIdAndUserData, user: User) => {
return (
<StepperBlock key={result.goalId} userName={userName} resultType="failed">
<StepperBlock
key={result.goalId}
userName={result.userData.name}
resultType="failed"
>
<Step
indicator={
<StepIndicator variant="solid" color="danger">
Expand All @@ -226,9 +208,13 @@ const failedStep = (result: GoalWithId, userName: string, user: UserData) => {
);
};

const pendingStep = (result: GoalWithId, userName: string, user: UserData) => {
const pendingStep = (result: GoalWithIdAndUserData, user: User) => {
return (
<StepperBlock key={result.goalId} userName={userName} resultType="pending">
<StepperBlock
key={result.goalId}
userName={result.userData.name}
resultType="pending"
>
<Step
active
indicator={
Expand Down Expand Up @@ -265,7 +251,7 @@ const GoalCard = ({
resultType?: "success" | "failed" | "pending";
goalId: string;
userId: string;
user: UserData;
user: User;
}) => {
const deadlineDate = new Date(deadline);
const currentDate = new Date();
Expand Down
30 changes: 11 additions & 19 deletions src/types/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export interface UserData {
export interface User {
userId: string;
name: string;
streak?: number;
Expand All @@ -8,34 +8,26 @@ export interface UserData {

export type LoginType = "Mail" | "Google" | "Guest" | "None";

export interface Post {
userId: string;
storedId: string;
text: string;
goalId: string;
submittedAt: Date | string;
}

export interface Goal {
userId: string;
deadline: Date | string;
text: string;
post?: Omit<Post, "submittedAt"> & { submittedAt: string };
}

export interface GoalWithId extends Goal {
export interface GoalWithIdAndUserData extends Goal {
goalId: string;
userData: User;
deadline: string;
}

export interface SuccessResult {
export interface Post {
userId: string;
storedURL: string;
text: string;
submittedAt: Date | string;
}

export interface PostWithGoalId extends Post {
goalId: string;
postId: string;
goalText: string;
postText: string;
storedId: string;
deadline: string;
submittedAt: string;
// dealineとsubmittedAtはAPIから取得するとString型になる
// Date型で使用したい場合はsrc\utils\DateFormatter.tsで変換
}
6 changes: 3 additions & 3 deletions src/utils/API/Post/createPost.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { appCheckToken, functionsEndpoint } from "@/app/firebase";
import { Post } from "@/types/types";
import { PostWithGoalId } from "@/types/types";

/**
* Cloud FunctionsのAPIを呼び出して、投稿をFirestoreに登録する
*
* @param {Post} postData
* @param {PostWithGoalId} postData
* @return {*}
*/
export const createPost = async (postData: Post) => {
export const createPost = async (postData: PostWithGoalId) => {
const response = await fetch(`${functionsEndpoint}/post/`, {
method: "POST",
headers: {
Expand Down
6 changes: 3 additions & 3 deletions src/utils/API/User/fetchUser.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { appCheckToken, functionsEndpoint } from "@/app/firebase";
import { UserData } from "@/types/types";
import { User } from "@/types/types";

/**
* Cloud FunctionsのAPIを呼び出して、ユーザー情報をFirestoreから取得する
*
* @param {string} userId
* @return {*} {Promise<UserData>}
* @return {*} {Promise<User>}
*/
export const fetchUserById = async (userId: string): Promise<UserData> => {
export const fetchUserById = async (userId: string): Promise<User> => {
const response = await fetch(`${functionsEndpoint}/user/id/${userId}`, {
method: "GET",
headers: {
Expand Down
Loading

0 comments on commit dccc6c2

Please sign in to comment.