Skip to content

Commit

Permalink
目標の状態を更新したら表示を自動で更新する
Browse files Browse the repository at this point in the history
  • Loading branch information
MurakawaTakuya committed Jan 16, 2025
1 parent eb45e30 commit e2015b5
Show file tree
Hide file tree
Showing 14 changed files with 255 additions and 94 deletions.
5 changes: 4 additions & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,8 @@
"sourceType": "module"
},
"plugins": ["react", "eslint-plugin-unused-imports"],
"rules": { "unused-imports/no-unused-imports": "error" }
"rules": {
"unused-imports/no-unused-imports": "error",
"@next/next/no-img-element": "off"
}
}
24 changes: 12 additions & 12 deletions functions/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,31 +64,31 @@ if (process.env.NODE_ENV === "production") {
});
}

// 10分間で最大100回に制限
// 10分間で最大200回に制限
app.use(
rateLimit({
windowMs: 10 * 60 * 1000,
max: 100,
keyGenerator: (req) => {
const key = req.headers["x-forwarded-for"] || req.ip || "unknown";
return Array.isArray(key) ? key[0] : key;
},
max: 200,
// keyGenerator: (req) => {
// const key = req.headers["x-forwarded-for"] || req.ip || "unknown";
// return Array.isArray(key) ? key[0] : key;
// },
handler: (req, res) => {
return res
.status(429)
.json({ message: "Too many requests, please try again later." });
},
})
);
// 1時間で最大300回に制限
// 1時間で最大500回に制限
app.use(
rateLimit({
windowMs: 60 * 60 * 1000,
max: 300,
keyGenerator: (req) => {
const key = req.headers["x-forwarded-for"] || req.ip || "unknown";
return Array.isArray(key) ? key[0] : key;
},
max: 500,
// keyGenerator: (req) => {
// const key = req.headers["x-forwarded-for"] || req.ip || "unknown";
// return Array.isArray(key) ? key[0] : key;
// },
handler: (req, res) => {
return res
.status(429)
Expand Down
Binary file modified public/img/blur.webp
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import { showSnackBar } from "@/Components/SnackBar/SnackBar";
import { PostWithGoalId } from "@/types/types";
import { createPost, handleCreatePostError } from "@/utils/API/Post/createPost";
import { useAddPost } from "@/utils/ResultContext";
import { removeImageMetadata, uploadImage } from "@/utils/Uploader";
import { useUser } from "@/utils/UserContext";
import { Add } from "@mui/icons-material";
Expand All @@ -24,20 +25,15 @@ import Typography from "@mui/material/Typography";
import { styled } from "@mui/material/styles";
import React, { ChangeEvent, useState } from "react";

export default function PostModal({
goalId,
setIsSubmitted,
}: {
goalId: string;
setIsSubmitted: React.Dispatch<React.SetStateAction<boolean>>;
}) {
export default function CreatePostModal({ goalId }: { goalId: string }) {
const [open, setOpen] = useState(false);
const [text, setText] = useState<string>("");
const [image, setImage] = useState<File | null>(null);
const [progress, setProgress] = useState<number>(100);
const [fileName, setFileName] = useState<string>("");
const [loading, setLoading] = useState<boolean>(false);
const { user } = useUser();
const addPost = useAddPost();

const handleTextChange = (event: ChangeEvent<HTMLInputElement>) => {
setText(event.target.value);
Expand All @@ -48,7 +44,7 @@ export default function PostModal({

if (!selectedFile) {
showSnackBar({
message: "ファイルが選択されていません",
message: "画像が選択されていません",
type: "warning",
});
return;
Expand Down Expand Up @@ -80,7 +76,7 @@ export default function PostModal({
const handleUpload = async () => {
if (!image) {
showSnackBar({
message: "ファイルが選択されていません",
message: "画像が選択されていません",
type: "warning",
});
return;
Expand Down Expand Up @@ -110,7 +106,12 @@ export default function PostModal({
message: "投稿しました",
type: "success",
});
setIsSubmitted(true);

const postDataWithStringDate = {
...postData,
submittedAt: (postData.submittedAt as Date).toISOString(),
};
addPost(goalId, postDataWithStringDate);

setImage(null);
setText("");
Expand Down
40 changes: 23 additions & 17 deletions src/Components/DashBoard/DashBoard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,6 @@ import CenterIn from "../Animation/CenterIn";
import Progress from "../Progress/Progress";
import styles from "./DashBoard.module.scss";

// eslint-disable-next-line @typescript-eslint/no-empty-function
let rerenderDashBoard: () => void = () => {};

export default function DashBoard({
userId = "",
success = true,
Expand All @@ -38,38 +35,40 @@ export default function DashBoard({
setFailedResults,
pendingResults,
setPendingResults,
lastPostDate,
setLastPostDate,
noMorePending,
setNoMorePending,
noMoreFinished,
setNoMoreFinished,
} = useResults();
const [noResult, setNoResult] = useState<boolean>(false);
const [isLoading, setIsLoading] = useState<boolean>(true);
const [reachedBottom, setReachedBottom] = useState<boolean>(false);
const [isLoadingMore, setIsLoadingMore] = useState<boolean>(false);
const bottomRef = useRef<HTMLDivElement>(null);
const isAlreadyFetching = useRef(false);
const [noMorePending, setNoMorePending] = useState<boolean>(false);
const [noMoreFinished, setNoMoreFinished] = useState<boolean>(false);

const [lastPostDate, setLastPostDate] = useState<string | null>(null); // 投稿が0の場合はnull

const { user } = useUser();
const myUserId = user?.userId;

const limit = 10; // limitずつ表示

const fetchData = () => {
if ((pending && noMorePending) || (success && failed && noMoreFinished)) {
return; // TODO: うまく動作していない
}
// すでにfetchしている場合はreturn
if (isAlreadyFetching.current) {
return;
} else {
isAlreadyFetching.current = true;
}
// 画面下に到達して既にロード中の場合はreturn
if (reachedBottom && !isLoadingMore) {
setIsLoadingMore(true);
}
const offset = pending
? pendingResults.length
: successResults.length + failedResults.length;
// TODO: offsetを実際に取得しにいった数だけでもいいかも
fetchResult({
userId,
success,
Expand Down Expand Up @@ -161,11 +160,15 @@ export default function DashBoard({
}, [isLoading, noMorePending, noMoreFinished, bottomRef.current, bottomRef]);

useEffect(() => {
rerenderDashBoard = fetchData;
if (userId) {
if (
(pending && pendingResults.length === 0) ||
(success && successResults.length === 0)
) {
fetchData();
} else {
setIsLoading(false);
}
}, [userId, success, failed, pending]);
}, [userId]);

useEffect(() => {
setNoResult(
Expand All @@ -180,6 +183,13 @@ export default function DashBoard({
return;
}

if (
(success && successResults.length > 0) ||
(pending && pendingResults.length > 0)
) {
return;
}

if (success && myUserId) {
// 最後に成功した目標を取得
fetchResult({
Expand Down Expand Up @@ -266,7 +276,3 @@ export default function DashBoard({
</>
);
}

export function triggerDashBoardRerender() {
rerenderDashBoard();
}
5 changes: 3 additions & 2 deletions src/Components/DeleteGoalModal/DeleteGoalModal.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use client";
import { appCheckToken, functionsEndpoint } from "@/app/firebase";
import { triggerDashBoardRerender } from "@/Components/DashBoard/DashBoard";
import { useDeleteGoal } from "@/utils/ResultContext";
import DeleteOutlineIcon from "@mui/icons-material/DeleteOutline";
import { DialogContent, DialogTitle, Modal, ModalDialog } from "@mui/joy";
import JoyButton from "@mui/joy/Button";
Expand All @@ -10,6 +10,7 @@ import { useState } from "react";
import { showSnackBar } from "../SnackBar/SnackBar";

export default function DeleteGoalModal({ goalId }: { goalId: string }) {
const deleleGoal = useDeleteGoal();
const [open, setOpen] = useState(false);

const handleDeleteGoal = async () => {
Expand All @@ -32,7 +33,7 @@ export default function DeleteGoalModal({ goalId }: { goalId: string }) {
message: "目標を削除しました",
type: "success",
});
triggerDashBoardRerender();
deleleGoal(goalId);
}
};

Expand Down
5 changes: 3 additions & 2 deletions src/Components/DeletePostModal/DeletePostModal.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
"use client";
import { appCheckToken, functionsEndpoint } from "@/app/firebase";
import { useDeletePost } from "@/utils/ResultContext";
import DeleteOutlineIcon from "@mui/icons-material/DeleteOutline";
import { DialogContent, DialogTitle, Modal, ModalDialog } from "@mui/joy";
import JoyButton from "@mui/joy/Button";
import Button from "@mui/material/Button";
import Stack from "@mui/material/Stack";
import { useState } from "react";
import { triggerDashBoardRerender } from "../DashBoard/DashBoard";
import { showSnackBar } from "../SnackBar/SnackBar";

export default function DeletePostModal({
Expand All @@ -16,6 +16,7 @@ export default function DeletePostModal({
goalId: string;
deadline: string;
}) {
const deletePost = useDeletePost();
const [open, setOpen] = useState(false);

const handleDeletePost = async () => {
Expand All @@ -38,7 +39,7 @@ export default function DeletePostModal({
message: "目標を削除しました",
type: "success",
});
triggerDashBoardRerender();
deletePost(goalId);
}
};

Expand Down
15 changes: 12 additions & 3 deletions src/Components/GoalModal/CreateGoalModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import { showSnackBar } from "@/Components/SnackBar/SnackBar";
import { Goal } from "@/types/types";
import { createGoal, handleCreateGoalError } from "@/utils/API/Goal/createGoal";
import { useAddGoal } from "@/utils/ResultContext";
import { useUser } from "@/utils/UserContext";
import AddIcon from "@mui/icons-material/Add";
import {
Expand All @@ -15,7 +16,6 @@ import {
Typography,
} from "@mui/joy";
import React, { useEffect, useState } from "react";
import { triggerDashBoardRerender } from "../DashBoard/DashBoard";

export default function CreateGoalModal({
open,
Expand All @@ -32,6 +32,7 @@ export default function CreateGoalModal({
const [deadline, setDeadline] = useState("");

const { user } = useUser();
const addResult = useAddGoal();

const resetDeadline = () => {
// 次の日の23時に設定
Expand Down Expand Up @@ -70,13 +71,21 @@ export default function CreateGoalModal({
};

try {
await createGoal(postData);
const data = await createGoal(postData);

showSnackBar({
message: "目標を作成しました",
type: "success",
});
triggerDashBoardRerender();

if (user) {
addResult({
...postData,
goalId: data.goalId,
userData: user,
deadline: deadline,
});
}

setText(defaultText || "");
setDeadline(defaultDeadline || resetDeadline());
Expand Down
9 changes: 3 additions & 6 deletions src/Components/Progress/PendingStep.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@ import { GoalWithIdAndUserData, User } from "@/types/types";
import AppRegistrationRoundedIcon from "@mui/icons-material/AppRegistrationRounded";
import Step from "@mui/joy/Step";
import StepIndicator from "@mui/joy/StepIndicator";
import { useState } from "react";
import CreatePostModal from "../CreatePostModal/CreatePostModal";
import CopyGoalAfterPostButton from "../GoalModal/CopyGoalAfterPostButton";
import PostModal from "../PostModal/PostModal";
import { GoalCard } from "./GoalCard";
import { StepperBlock } from "./StepperBlock";

Expand All @@ -15,8 +14,6 @@ export const PendingStep = ({
result: GoalWithIdAndUserData;
user: User;
}) => {
const [isSubmitted, setIsSubmitted] = useState(false);

return (
<StepperBlock key={result.goalId} result={result} resultType="pending">
<Step
Expand All @@ -37,7 +34,7 @@ export const PendingStep = ({
user={user}
/>

{isSubmitted ? (
{result.post ? (
// 投稿したら同じ目標で明日にも作成できるボタンを表示する
<CopyGoalAfterPostButton
goalText={result.text}
Expand All @@ -46,7 +43,7 @@ export const PendingStep = ({
) : (
// 自分の作成した目標の場合のみ投稿可能にする
result.userId === user?.userId && (
<PostModal goalId={result.goalId} setIsSubmitted={setIsSubmitted} />
<CreatePostModal goalId={result.goalId} />
)
)}
</Step>
Expand Down
Loading

0 comments on commit e2015b5

Please sign in to comment.