diff --git a/src/features/rolling-paper/components/rolling-paper-list.jsx b/src/features/rolling-paper/components/rolling-paper-list.jsx
index e71e048..7f91ba3 100644
--- a/src/features/rolling-paper/components/rolling-paper-list.jsx
+++ b/src/features/rolling-paper/components/rolling-paper-list.jsx
@@ -248,8 +248,15 @@ function RollingPaperList({ cardData, totalPages, currentPage, onTurnCards }) {
const navigate = useNavigate();
const handleCardClick = (cardId) => {
+ if(!cardId) {
+ navigate("*");
+ return;
+ }
+
navigate(`/post/${cardId}`);
};
+
+
const profileImages = useMemo(
() =>
cardData.flatMap((card) =>
diff --git a/src/hooks/use-image-loader.jsx b/src/hooks/use-image-loader.jsx
index edfde97..f4dc4db 100644
--- a/src/hooks/use-image-loader.jsx
+++ b/src/hooks/use-image-loader.jsx
@@ -29,36 +29,51 @@ function useImageListLodeChecker(imageList = []) {
useEffect(() => {
if (!imageList.length) return;
+ let cancelled = false;
+
setImageLoadStates((prev) => {
const nextStates = { ...prev };
imageList.forEach(({ id }) => {
- if (nextStates[id] === undefined) {
- nextStates[id] = false;
- }
+ if (nextStates[id] === undefined) nextStates[id] = false;
});
return nextStates;
});
+ const imageMap = {};
+
imageList.forEach(({ id, backgroundImageURL }) => {
setImageLoadStates((prev) => {
- if (prev[id]) return prev;
+ if (prev[id] === true) return prev;
- if (!backgroundImageURL) {
- return { ...prev, [id]: false };
- }
+ if (!backgroundImageURL) return { ...prev, [id]: false };
const img = new Image();
- img.src = backgroundImageURL;
+ imageMap[id] = img;
+
img.onload = () => {
- setImageLoadStates((p) => ({ ...p, [id]: true }));
+ if (!cancelled) {
+ setImageLoadStates((p) => ({ ...p, [id]: true }));
+ }
};
+
img.onerror = () => {
- setImageLoadStates((p) => ({ ...p, [id]: false }));
+ if (!cancelled) {
+ setImageLoadStates((p) => ({ ...p, [id]: false }));
+ }
};
+ img.src = backgroundImageURL;
return prev;
});
});
+
+ return () => {
+ cancelled = true;
+ Object.values(imageMap).forEach((img) => {
+ img.onload = null;
+ img.onerror = null;
+ });
+ };
}, [imageList]);
return imageLoadStates;
diff --git a/src/pages/404-page.jsx b/src/pages/404-page.jsx
index e0354cd..d88d9c9 100644
--- a/src/pages/404-page.jsx
+++ b/src/pages/404-page.jsx
@@ -45,20 +45,20 @@ const ErrorNumber = styled.h1`
font-weight: 900;
color: var(--color-purple-700);
margin: 0;
- font-size: 200px;
+ font-size: 150px;
${media.mobile} {
- font-size: 130px;
+ font-size: 110px;
}
`;
const ErrorComment = styled.span`
- font-size: 80px;
+ font-size: 70px;
color: #6e6293;
font-weight: 700;
em {
- font-size: 100px;
+ font-size: 90px;
font-weight: 800;
font-style: normal;
color: #240079;
@@ -72,24 +72,20 @@ const ErrorComment = styled.span`
}
${media.mobile} {
- font-size: 50px;
+ font-size: 40px;
em {
- font-size: 60px;
+ font-size: 50px;
}
}
`;
const ErrorCommentSofter = styled.span`
- font-size: 50px;
+ font-size: 25px;
color: #6e6293;
font-weight: 400;
- ${media.tablet} {
- font-size: 40px;
- }
-
${media.mobile} {
- font-size: 30px;
+ font-size: 18px;
}
`;
@@ -133,10 +129,10 @@ const Error404Page = () => {
404
- Oops! Page Not Found...
+ 앗 이런! 페이지를 찾을 수 없습니다.
- Don't worry, let's get you back on track.
+ 페이지의 주소가 올바르지 않거나, 삭제 또는 다른 페이지로 변경되었습니다.
setShowsToast(false);
+
const colorOptions = [
{ label: "beige", color: BACKGROUND_COLOR.beige },
{ label: "purple", color: BACKGROUND_COLOR.purple },
@@ -137,7 +146,8 @@ function CreatePostPage() {
navigate(`/post/${newPostId}`);
} catch (error) {
console.error("게시물 생성 실패:", error);
- alert("게시물 생성에 실패했습니다. 다시 시도해 주세요");
+ setToastMessage("게시물 생성에 실패했습니다. 다시 시도해 주세요.")
+ setShowsToast(true);
} finally {
setLoading(false);
}
@@ -189,6 +199,15 @@ function CreatePostPage() {
onClick={handleCreate}
/>
+
+ {showsToast&&
+
+ }
);
}
diff --git a/src/pages/messages-page.jsx b/src/pages/messages-page.jsx
index 25f37c9..a15245f 100644
--- a/src/pages/messages-page.jsx
+++ b/src/pages/messages-page.jsx
@@ -25,6 +25,8 @@ import { useMedia } from "../hooks/use-media";
import { useModalDialog } from "../hooks/use-modal-dialog";
import ContentLayout from "../layouts/content-layout";
import { media } from "../utils/media";
+import Toast from "../components/toast/toast";
+import { useToast } from "../hooks/use-toast";
const Content = styled.div`
& > div {
@@ -111,6 +113,8 @@ function MessagesPage() {
onPrimaryAction,
onDismissDialog,
} = useModalDialog();
+ const [toastMessage, setToastMessage] = useState();
+ const [hasMoreMessages, setHasMoreMessages] = useState(true);
const isEditing = useMemo(
() => location.pathname.includes("edit"),
@@ -121,6 +125,12 @@ function MessagesPage() {
navigate("edit");
};
+ const { showsToast, isOpen, setShowsToast, onDismiss } = useToast({
+ timeout: 5000,
+ });
+
+ const handleToastCloseClick = () => setShowsToast(false);
+
const handleRollingPaperDelete = () => {
openDialog({
title: `${recipient.name} 님의 롤링 페이퍼를 삭제할까요?`,
@@ -130,8 +140,9 @@ function MessagesPage() {
await deleteRecipient({ id: recipient.id });
navigate(`/list`);
} catch (error) {
- // TODO: Error 처리
console.log(error);
+ setToastMessage(error);
+ setShowsToast(true);
}
},
});
@@ -152,8 +163,9 @@ function MessagesPage() {
prev.filter((prevMessage) => prevMessage.id !== message.id)
);
} catch (error) {
- // TODO: Error 처리
console.log(error);
+ setToastMessage(error);
+ setShowsToast(true);
}
},
});
@@ -168,8 +180,11 @@ function MessagesPage() {
};
const handleInfiniteScroll = async () => {
- const messages = await getNextPageMessages();
- if (!messages) return;
+ if (!hasMoreMessages) return;
+
+ try {
+ const messages = await getNextPageMessages();
+ if (!messages) return;
setMessages((prev) => {
const newMessages = [...prev];
@@ -181,6 +196,12 @@ function MessagesPage() {
return newMessages;
});
+ } catch(error) {
+ console.error(error);
+ setToastMessage(error);
+ setShowsToast(true);
+ setHasMoreMessages(false);
+ }
};
useEffect(() => {
@@ -196,11 +217,12 @@ function MessagesPage() {
} catch (error) {
// TODO: Error 처리 필요
console.error(error);
+ navigate("/notfound", { replace: true });
}
}
fetchRollingPaper();
- }, [id]);
+ }, [id, navigate]);
const content = (
<>
@@ -270,6 +292,15 @@ function MessagesPage() {
}
/>
+
+ {showsToast&&
+
+ }
);
}
diff --git a/src/pages/rolling-paper-list-page.jsx b/src/pages/rolling-paper-list-page.jsx
index 7e780dc..1f6f507 100644
--- a/src/pages/rolling-paper-list-page.jsx
+++ b/src/pages/rolling-paper-list-page.jsx
@@ -7,6 +7,8 @@ import BUTTON_SIZE from "../components/button/button-size";
import RollingPaperList from "../features/rolling-paper/components/rolling-paper-list";
import { useMedia } from "../hooks/use-media";
import { media } from "../utils/media";
+import Toast from "../components/toast/toast";
+import { useToast } from "../hooks/use-toast";
const TopContainer = styled.div`
text-align: center;
@@ -71,9 +73,8 @@ function getCachedImage(url) {
return cache[url].src;
}
-function RollingPaperListPage() {
- const navigate = useNavigate();
+function RollingPaperListPage() {
const [recipientsData, setRecipientsData] = useState([]);
const [popularDataList, setPopularDataList] = useState([]);
const [recentDataList, setRecentDataList] = useState([]);
@@ -81,6 +82,15 @@ function RollingPaperListPage() {
const [recentCurrentPage, setRecentCurrentPage] = useState(0);
const [cardCount, setCardCount] = useState(4);
const { isDesktop } = useMedia();
+ const [isCalledApi, setIsCalledApi] = useState(true);
+ const [toastMessage, setToastMessage] = useState();
+ const navigate = useNavigate();
+
+ const { showsToast, isOpen, setShowsToast, onDismiss } = useToast({
+ timeout: 5000,
+ });
+
+ const handleToastCloseClick = () => setShowsToast(false);
useEffect(() => {
isDesktop ? setCardCount(4) : setCardCount(null);
@@ -90,16 +100,24 @@ function RollingPaperListPage() {
navigate("/post");
};
+
useEffect(() => {
- apiClient
- .get("/recipients/")
- .then((res) => {
+ if (!isCalledApi) return;
+
+ const fetchData = async () => {
+ try {
+ const res = await apiClient.get("/recipients/");
setRecipientsData(res.data.results);
- })
- .catch((err) => {
+ } catch (err) {
console.error("오류:", err);
- });
- }, []);
+ setToastMessage("메시지를 가져오는 중 오류가 발생했어요.")
+ setShowsToast(true);
+ } finally {
+ setIsCalledApi(false);
+ }
+ };
+ fetchData();
+ }, [isCalledApi, setShowsToast]);
useEffect(() => {
recipientsData.forEach((data) => {
@@ -183,6 +201,15 @@ function RollingPaperListPage() {
onClick={handleMakingButton}
/>
+
+ {showsToast&&
+
+ }
);
}
diff --git a/src/pages/send-message-page.jsx b/src/pages/send-message-page.jsx
index 79e134a..aa97c67 100644
--- a/src/pages/send-message-page.jsx
+++ b/src/pages/send-message-page.jsx
@@ -12,6 +12,8 @@ import TextEditor from "../components/text-editor/text-editor";
import TextField from "../components/text-field/text-field";
import TEXT_FIELD_TYPE from "../components/text-field/text-field-type";
import { media } from "../utils/media";
+import Toast from "../components/toast/toast";
+import { useToast } from "../hooks/use-toast";
const SendContainer = styled.div`
display: flex;
@@ -110,8 +112,15 @@ function SendMessagePage() {
title: "Noto Sans",
fontFamily: "Noto Sans",
});
+ const [toastMessage, setToastMessage] = useState();
const navigate = useNavigate();
+ const { showsToast, isOpen, setShowsToast, onDismiss } = useToast({
+ timeout: 5000,
+ });
+
+ const handleToastCloseClick = () => setShowsToast(false);
+
const handleChange = (e) => {
const value = e.target.value;
setName(value);
@@ -169,6 +178,8 @@ function SendMessagePage() {
navigate(`/post/${recipientId}`);
} catch (error) {
console.error("메시지 저장 실패:", error);
+ setToastMessage("메시지를 저장하는 중 오류가 발생했어요.")
+ setShowsToast(true);
}
};
@@ -264,6 +275,15 @@ function SendMessagePage() {
onClick={handleCreate}
/>
+
+ {showsToast&&
+
+ }
);
}