diff --git a/bookduck/package.json b/bookduck/package.json
index 00bf08d3..8806b8e6 100644
--- a/bookduck/package.json
+++ b/bookduck/package.json
@@ -14,7 +14,7 @@
"@react-spring/web": "^9.7.5",
"@react-three/drei": "^9.115.0",
"@react-three/fiber": "^8.17.10",
- "@tanstack/react-query": "^5.60.2",
+ "@tanstack/react-query": "^5.61.3",
"axios": "^1.7.7",
"classnames": "^2.5.1",
"html2canvas": "^1.4.1",
diff --git a/bookduck/src/App.jsx b/bookduck/src/App.jsx
index 77e70070..9022220f 100644
--- a/bookduck/src/App.jsx
+++ b/bookduck/src/App.jsx
@@ -33,17 +33,29 @@ import SettingPage from "./pages/SettingPage/SettingPage";
import FriendListPage from "./pages/FriendPage/FriendListPage";
import OAuthRedierctPage from "./pages/LoginPage/OAuthRedierctPage";
import OtherMainPage from "./pages/OtherUserPage/OtherMainPage";
+import handleFcmToken from "./components/NotificationPage/handleFcmToken";
+import { getUserId } from "./api/oauth";
function App() {
useEffect(() => {
- const fetchFcmToken = async () => {
- const token = await requestFcmToken();
- if (token) {
- console.log("FCM 토큰을 서버로 전송하거나 저장:", token);
+ const fetchandSendFCM = async () => {
+ try {
+ const id = await getUserId();
+ const accessToken = localStorage.getItem("token");
+
+ if (!id || !accessToken) {
+ console.error("사용자 ID 또는 액세스 토큰이 없습니다.");
+ return;
+ }
+ await handleFcmToken(id);
+ } catch (error) {
+ console.error("FCM 처리 중 오류 발생:", error.message);
}
};
- fetchFcmToken();
+
+ fetchandSendFCM();
}, []);
+
useEffect(() => {
// 포그라운드 메시지 수신 처리
onMessage(messaging, (payload) => {
diff --git a/bookduck/src/api/fcm.js b/bookduck/src/api/fcm.js
index 491d2cf4..19958871 100644
--- a/bookduck/src/api/fcm.js
+++ b/bookduck/src/api/fcm.js
@@ -18,7 +18,6 @@ export const requestFcmToken = async () => {
});
if (fcmToken) {
- console.log("FCM 토큰:", fcmToken);
return fcmToken;
} else {
console.error("FCM 토큰을 가져오지 못했습니다.");
diff --git a/bookduck/src/api/fcmApi.js b/bookduck/src/api/fcmApi.js
index fc68adea..f26ae445 100644
--- a/bookduck/src/api/fcmApi.js
+++ b/bookduck/src/api/fcmApi.js
@@ -1,12 +1,14 @@
import { post } from "./example";
-export const sendFcmToken = async (userId, fcmToken) => {
+import { postAccessTokenIssue } from "./oauth";
+export const postFcmToken = async (userId, fcmToken) => {
try {
// POST 요청으로 FCM 토큰 전송
- const response = await post(`/fcm/${userId}/token`, { fcmToken });
+ const response = await post(`/fcm/${userId}/token`, {
+ fcmToken: fcmToken,
+ });
console.log("FCM 토큰 전송 성공:", response);
return response;
} catch (error) {
console.error("FCM 토큰 전송 실패:", error);
- throw error;
}
};
diff --git a/bookduck/src/assets/otherUserPage/readingspace-duck.svg b/bookduck/src/assets/otherUserPage/readingspace-duck.svg
new file mode 100644
index 00000000..aa7055a7
--- /dev/null
+++ b/bookduck/src/assets/otherUserPage/readingspace-duck.svg
@@ -0,0 +1,19 @@
+
diff --git a/bookduck/src/components/MainPage/ReadingSpaceComponent.jsx b/bookduck/src/components/MainPage/ReadingSpaceComponent.jsx
index 1ee6ff41..d5276af4 100644
--- a/bookduck/src/components/MainPage/ReadingSpaceComponent.jsx
+++ b/bookduck/src/components/MainPage/ReadingSpaceComponent.jsx
@@ -11,13 +11,13 @@ import goEdit from "../../assets/mainPage/go-edit.svg";
import goRight from "../../assets/mainPage/go-right.svg";
import cancel from "../../assets/mainPage/cancel.svg";
import menu from "../../assets/mainPage/menu-vertical.svg";
-import recordCircleIcon from "../../assets/recordingPage/record-circle-icon.svg";
import editIcon from "../../assets/bookinfoPage/edit.svg";
import deleteIcon from "../../assets/bookinfoPage/trash.svg";
import plusIcon from "../../assets/mainPage/plus.svg";
import helpCircle from "../../assets/mainPage/help-circle.svg";
import DraggableList from "./DraggableList";
import { getUserId } from "../../api/oauth";
+import ReadingSpaceDuck from "../../assets/otherUserPage/readingspace-duck.svg";
const ReadingSpaceComponent = ({
setColor,
@@ -56,9 +56,8 @@ const ReadingSpaceComponent = ({
try {
const id = isMine ? await getUserId() : otherUserId;
setUserId(id);
-
const response = await get(`/users/${id}/readingspace`);
- console.log(response);
+ // console.log(response);
setCards(response.cardList);
} catch (error) {
console.error("리딩스페이스 조회 오류", error);
@@ -101,14 +100,6 @@ const ReadingSpaceComponent = ({
getCards();
}, []);
- useEffect(() => {
- console.log(cards);
- }, [cards]);
-
- useEffect(() => {
- console.log("otehruseif", otherUserId);
- }, [otherUserId]);
-
useEffect(() => {
if (isAllDelete) {
patchCards([]).then(() => {
@@ -135,8 +126,10 @@ const ReadingSpaceComponent = ({
const handleMenuClick = () => {
if (height.get() < expandedHeight) {
api.start({ height: expandedHeight });
+ setBottomSheetShow(true);
+ } else {
+ setBottomSheetShow(true);
}
- setBottomSheetShow(true);
};
const handleEditClick = () => {
@@ -287,7 +280,12 @@ const ReadingSpaceComponent = ({
-
- 리딩 스페이스가 텅 비어있네요!
+ isMine ? (
+ // 카드가 없을 때 렌더링
+
+
+ 리딩 스페이스가 텅 비어있네요!
+
+
+ 나만의 리딩 스페이스를 꾸며보세요
+
+
navigate("selectcard")}
+ />
-
- 나만의 리딩 스페이스를 꾸며보세요
+ ) : (
+
+
+ 리딩스페이스가 텅 비어있어요.
+
+
-
navigate("selectcard")}
- />
-
+ )
) : (
// 카드가 있을 때 렌더링
)}
- {isFloatingVisible && (
+ {/* {isFloatingVisible && (
navigate("/selectbook")}
@@ -360,7 +367,7 @@ const ReadingSpaceComponent = ({
alt="record_circle_icon"
/>
- )}
+ )} */}
diff --git a/bookduck/src/components/NotificationPage/GeneralNotiComponent.jsx b/bookduck/src/components/NotificationPage/GeneralNotiComponent.jsx
index f8832eaa..7b94ce4c 100644
--- a/bookduck/src/components/NotificationPage/GeneralNotiComponent.jsx
+++ b/bookduck/src/components/NotificationPage/GeneralNotiComponent.jsx
@@ -1,123 +1,101 @@
-import React, { useState, useEffect, useRef } from "react";
+import React, { useRef } from "react";
+import {
+ useInfiniteQuery,
+ useMutation,
+ useQueryClient,
+} from "@tanstack/react-query";
import { get, patch } from "../../api/example";
import NotificationItemComponent from "./NotificationItemComponent";
import { useSSE } from "../../context/SSEProvider";
-/*API-일반 알람 읽음 처리*/
+
+/* API - 일반 알람 읽음 처리 */
export const patchAlarm = async (alarmId) => {
- try {
- await patch(`/alarms/common`, {
- alarmId: alarmId,
- });
- console.log("일반 알림 읽음 처리 완료");
- } catch (error) {
- console.error("일반 알람 읽음 처리 에러", error);
- }
+ await patch(`/alarms/common`, { alarmId });
};
-const GeneralNotiComponent = () => {
- const [notifications, setNotifications] = useState([]);
- const [currentPage, setCurrentPage] = useState(0);
- const [totalPages, setTotalPages] = useState(1);
- const [isLoading, setIsLoading] = useState(false);
- const loaderRef = useRef(null);
- const DATA_LIMIT = 10;
- const { sseData } = useSSE();
+/* API - 알람 리스트 받기 */
+export const fetchAlarmList = async ({ pageParam = 0 }) => {
+ const DATA_LIMIT = 10;
+ const response = await get(
+ `/alarms/common?page=${pageParam}&size=${DATA_LIMIT}`
+ );
+ const { pageContent, totalPages } = response;
- /* API-알람 리스트 받기*/
- const getAlarmList = async (page = 0) => {
- try {
- const response = await get(
- `/alarms/common?page=${page}&size=${DATA_LIMIT}`
- );
- console.log("response", response);
- const data = response.pageContent.map((alarm) => ({
- resourceId: alarm.resourceId,
- alarmId: alarm.alarmId,
- isRead: alarm.isRead,
- alarmType: alarm.alarmType,
- createdTime: alarm.createdTime,
- boldText: alarm.boldText,
- }));
- setNotifications((n) => [...n, ...data]);
- setTotalPages(response.totalPages || 1);
- } catch (error) {
- console.error("알람 읽기 오류", error);
- }
+ return {
+ pageContent,
+ nextPage: pageParam + 1,
+ totalPages: totalPages || 1,
};
+};
+
+const GeneralNotiComponent = () => {
+ const loaderRef = useRef(null);
+ const queryClient = useQueryClient();
+ const { sseData } = useSSE();
- // 초기화 로직
- useEffect(() => {
- const initialize = async () => {
- setNotifications([]); // 기존 데이터 초기화
- setCurrentPage(0); // 첫 페이지로 초기화
- setIsLoading(true);
- await getAlarmList(0);
- setIsLoading(false);
- };
+ // React Query - 알람 리스트 가져오기
+ const { data, isLoading, isFetchingNextPage, fetchNextPage, hasNextPage } =
+ useInfiniteQuery({
+ queryKey: ["alarmList"],
+ queryFn: fetchAlarmList,
+ getNextPageParam: (lastPage) =>
+ lastPage.nextPage < lastPage.totalPages ? lastPage.nextPage : undefined,
+ });
- initialize();
- }, []);
+ // React Query - 알람 읽음 처리
+ const { mutate: markAsRead } = useMutation({
+ mutationFn: patchAlarm,
+ onSuccess: () => {
+ queryClient.invalidateQueries({ queryKey: ["alarmList"] });
+ },
+ });
// SSE 데이터 처리
- useEffect(() => {
+ React.useEffect(() => {
if (!sseData.isCommonAlarmChecked) {
- console.log("새로운 일반 알람 확인");
- const newAlarm = {
- alarmId: sseData.alarmId,
- isRead: sseData.isRead,
- alarmType: sseData.alarmType,
- createdTime: sseData.createdTime,
- boldText: sseData.boldText,
- };
- setNotifications((prev) => [newAlarm, ...prev]);
+ queryClient.invalidateQueries({ queryKey: ["alarmList"] });
}
- }, [sseData]);
-
- // 무한 스크롤 감지
- useEffect(() => {
- const handleObserver = (entries) => {
- const [entry] = entries;
- if (entry.isIntersecting && currentPage < totalPages && !isLoading) {
- console.log("다음 페이지 로드");
- setCurrentPage((p) => p + 1);
- }
- };
+ }, [sseData, queryClient]);
- const observer = new IntersectionObserver(handleObserver, {
- root: null,
- rootMargin: "0px",
- threshold: 1.0,
- });
+ // Intersection Observer - 무한 스크롤
+ React.useEffect(() => {
+ const observer = new IntersectionObserver(
+ (entries) => {
+ const [entry] = entries;
+ if (entry.isIntersecting && hasNextPage && !isFetchingNextPage) {
+ fetchNextPage();
+ }
+ },
+ { root: null, rootMargin: "0px", threshold: 1.0 }
+ );
if (loaderRef.current) observer.observe(loaderRef.current);
return () => observer.disconnect();
- }, [currentPage, totalPages, isLoading]);
+ }, [fetchNextPage, hasNextPage, isFetchingNextPage]);
- // 현재 페이지 데이터 로드
- useEffect(() => {
- if (currentPage > 0 && !isLoading) {
- console.log(`페이지 ${currentPage} 데이터 로드`);
- setIsLoading(true);
- getAlarmList(currentPage).finally(() => setIsLoading(false));
- }
- }, [currentPage]);
+ if (isLoading) {
+ return
로딩 중...
;
+ }
return (
<>
- {notifications.map((notification, index) => (
-
-
-
- ))}
-
+ {data.pages.map((page, pageIndex) =>
+ page.pageContent.map((notification, index) => (
+
+ markAsRead(notification.alarmId)}
+ />
+
+ ))
+ )}
+
{isFetchingNextPage &&
로딩 중...
}
>
);
};
diff --git a/bookduck/src/components/NotificationPage/NotificationItemComponent.jsx b/bookduck/src/components/NotificationPage/NotificationItemComponent.jsx
index cbf5d570..9e264592 100644
--- a/bookduck/src/components/NotificationPage/NotificationItemComponent.jsx
+++ b/bookduck/src/components/NotificationPage/NotificationItemComponent.jsx
@@ -99,8 +99,6 @@ const NotificationItemComponent = ({
}) => {
const navigate = useNavigate();
const notificationData = notificationTemplates[alarmType];
- console.log(notificationData);
-
useEffect(() => {
console.log("resourceId", resourceId);
}, [resourceId]);
diff --git a/bookduck/src/components/NotificationPage/handleFcmToken.js b/bookduck/src/components/NotificationPage/handleFcmToken.js
index f2655e91..f572747d 100644
--- a/bookduck/src/components/NotificationPage/handleFcmToken.js
+++ b/bookduck/src/components/NotificationPage/handleFcmToken.js
@@ -1,19 +1,24 @@
import { requestFcmToken } from "../../api/fcm";
-import { sendFcmToken } from "../../api/fcmApi";
-const handleFcmToken = async (userId) => {
- try {
- // 1. FCM 토큰 가져오기
- const fcmToken = await requestFcmToken();
+import { postFcmToken } from "../../api/fcmApi";
- if (fcmToken) {
- // 2. 서버로 FCM 토큰 전송
- await sendFcmToken(userId, fcmToken);
- console.log("FCM 토큰 서버로 전송 완료");
- } else {
- console.error("FCM 토큰 가져오기 실패");
+const handleFcmToken = async (userId) => {
+ const isTokenSent = JSON.parse(localStorage.getItem("isFcmTokenSent"));
+ if (!isTokenSent && userId) {
+ try {
+ // 1. FCM 토큰 가져오기
+ const fcmToken = await requestFcmToken();
+ if (fcmToken) {
+ // 2. 서버로 FCM 토큰 전송
+ await postFcmToken(userId, fcmToken);
+ localStorage.setItem("isFcmTokenSent", JSON.stringify(true));
+ } else {
+ console.error("FCM 토큰 가져오기 실패");
+ }
+ } catch (error) {
+ console.error("FCM 처리 중 오류 발생:", error);
}
- } catch (error) {
- console.error("FCM 처리 중 오류 발생:", error);
+ } else {
+ // console.log("FCM토큰이 이미 저장되었습니다.");
}
};
diff --git a/bookduck/src/components/OtherUserPage/Header.jsx b/bookduck/src/components/OtherUserPage/Header.jsx
index 9bdad60c..557f0786 100644
--- a/bookduck/src/components/OtherUserPage/Header.jsx
+++ b/bookduck/src/components/OtherUserPage/Header.jsx
@@ -1,16 +1,13 @@
import React from "react";
import { useNavigate } from "react-router-dom";
import backIcon from "../../assets/otherUserPage/back.svg";
-const Header = ({ name, isFriend = true, handleAddClick }) => {
+const Header = ({ isFriend = true, handleAddClick }) => {
const navigate = useNavigate();
return (
-
-
-
{name}의 홈
-
+
{isFriend ? (
{
const [tab, setTab] = useState("일반");
const { sseData } = useSSE();
- const [dotStates, setDotStates] = useState([
- !sseData.isCommonAlarmChecked,
- !sseData.isAnnouncementChecked,
- ]);
+ const [dotStates, setDotStates] = useState([false, false]);
useEffect(() => {
- const newDotStates = [
- !sseData.isCommonAlarmChecked,
- !sseData.isAnnouncementChecked,
- ];
- console.log("업데이트된 dotStates:", newDotStates);
- setDotStates(newDotStates);
+ if (sseData) {
+ const newDotStates = [
+ sseData.isCommonAlarmChecked === null
+ ? null
+ : !sseData.isCommonAlarmChecked,
+ sseData.isAnnouncementChecked === null
+ ? null
+ : !sseData.isAnnouncementChecked,
+ ];
+ console.log("업데이트된 dotStates:", newDotStates);
+ setDotStates(newDotStates);
+ }
}, [sseData]);
return (
diff --git a/bookduck/src/pages/OtherUserPage/OtherMainPage.jsx b/bookduck/src/pages/OtherUserPage/OtherMainPage.jsx
index 2d61d8a3..d0af928a 100644
--- a/bookduck/src/pages/OtherUserPage/OtherMainPage.jsx
+++ b/bookduck/src/pages/OtherUserPage/OtherMainPage.jsx
@@ -2,10 +2,15 @@ import React, { useState, useEffect } from "react";
import Header from "../../components/OtherUserPage/Header";
import StatusBar from "../../components/common/StatusBar";
import { get, post } from "../../api/example";
+import { useNavigate } from "react-router-dom";
import ReadingSpaceComponent from "../../components/MainPage/ReadingSpaceComponent";
+import BookCountDisplay from "../../components/MainPage/BookCountDisplay";
+import right from "../../assets/common/right-yellow.svg";
+import mainDuck from "../../assets/common/main-duck.svg";
import { useParams } from "react-router-dom";
const OtherMainPage = () => {
+ const navigate = useNavigate();
let { id: userId } = useParams();
const [userInfo, setUserInfo] = useState([]);
//API 연결-정보받기
@@ -50,14 +55,47 @@ const OtherMainPage = () => {
}
}, [userId]);
return (
-
+
);
};
diff --git a/bookduck/src/pages/OtherUserPage/OtherStatisticsPage.jsx b/bookduck/src/pages/OtherUserPage/OtherStatisticsPage.jsx
new file mode 100644
index 00000000..5cab5603
--- /dev/null
+++ b/bookduck/src/pages/OtherUserPage/OtherStatisticsPage.jsx
@@ -0,0 +1,9 @@
+import React from "react";
+import { useNavigate } from "react-router-dom";
+const OtherStatisticsPage = () => {
+ const navigate = useNavigate();
+ let { id: userId } = useParams();
+ return
;
+};
+
+export default OtherStatisticsPage;
diff --git a/bookduck/src/pages/SearchPage/SearchMainPage.jsx b/bookduck/src/pages/SearchPage/SearchMainPage.jsx
index dc1f6492..10c71dbf 100644
--- a/bookduck/src/pages/SearchPage/SearchMainPage.jsx
+++ b/bookduck/src/pages/SearchPage/SearchMainPage.jsx
@@ -33,7 +33,7 @@ const SearchMainPage = () => {
const getPopularBooks = async () => {
try {
const response = await get(`/bookinfo/most`);
- console.log("많이 읽는", response);
+ // console.log("많이 읽는", response);
setPopularBooks(response.bookList);
} catch (error) {
console.error("많이 읽는 책 읽기 오류", error);
diff --git a/bookduck/src/pages/SettingPage/SettingPage.jsx b/bookduck/src/pages/SettingPage/SettingPage.jsx
index 35ceeff4..7425dc2e 100644
--- a/bookduck/src/pages/SettingPage/SettingPage.jsx
+++ b/bookduck/src/pages/SettingPage/SettingPage.jsx
@@ -215,7 +215,7 @@ const SettingPage = () => {
const handleLogout = async () => {
try {
await postLogout();
- localStorage.removeItem("token");
+ localStorage.clear();
navigate(`/login`, { replace: true });
} catch (error) {
console.error(error);
diff --git a/bookduck/yarn.lock b/bookduck/yarn.lock
index c20e7d81..762ea274 100644
--- a/bookduck/yarn.lock
+++ b/bookduck/yarn.lock
@@ -1986,17 +1986,17 @@
"@svgr/plugin-jsx" "8.1.0"
"@svgr/plugin-svgo" "8.1.0"
-"@tanstack/query-core@5.59.20":
- version "5.59.20"
- resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-5.59.20.tgz#356718976536727b9af0ad1163a21fd6a44ee0a9"
- integrity sha512-e8vw0lf7KwfGe1if4uPFhvZRWULqHjFcz3K8AebtieXvnMOz5FSzlZe3mTLlPuUBcydCnBRqYs2YJ5ys68wwLg==
+"@tanstack/query-core@5.60.6":
+ version "5.60.6"
+ resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-5.60.6.tgz#0dd33fe231b0d18bf66d0c615b29899738300658"
+ integrity sha512-tI+k0KyCo1EBJ54vxK1kY24LWj673ujTydCZmzEZKAew4NqZzTaVQJEuaG1qKj2M03kUHN46rchLRd+TxVq/zQ==
-"@tanstack/react-query@^5.60.2":
- version "5.60.2"
- resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-5.60.2.tgz#a65db96702c11f3f868c40372ce66f05c2a77cc6"
- integrity sha512-JhpJNxIAPuE0YCpP1Py4zAsgx+zY0V531McRMtQbwVlJF8+mlZwcOPrzGmPV248K8IP+mPbsfxXToVNMNwjUcw==
+"@tanstack/react-query@^5.61.3":
+ version "5.61.3"
+ resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-5.61.3.tgz#0187b73b87adaeaed09f3d9717e35b507175fe23"
+ integrity sha512-c3Oz9KaCBapGkRewu7AJLhxE9BVqpMcHsd3KtFxSd7FSCu2qGwqfIN37zbSGoyk6Ix9LGZBNHQDPI6GpWABnmA==
dependencies:
- "@tanstack/query-core" "5.59.20"
+ "@tanstack/query-core" "5.60.6"
"@trysound/sax@0.2.0":
version "0.2.0"