);
})}
diff --git a/src/mainPage/features/interactions/v2l/PuzzlePiece.jsx b/src/mainPage/features/interactions/v2l/PuzzlePiece.jsx
index 5f644c55..ebc3d223 100644
--- a/src/mainPage/features/interactions/v2l/PuzzlePiece.jsx
+++ b/src/mainPage/features/interactions/v2l/PuzzlePiece.jsx
@@ -1,14 +1,14 @@
import { useState } from "react";
import { LINEAR } from "./constants.js";
-function PuzzlePiece({ shape, onClick, fixRotate }) {
+function PuzzlePiece({ shape, onClick, fixRotate, ariaLabel }) {
const [fixing, setFixing] = useState(false);
const style = {
transform: `rotate( ${shape.rotate * 90}deg)`,
};
return (
-
+
);
}
diff --git a/src/mainPage/features/interactions/v2l/utils.js b/src/mainPage/features/interactions/v2l/utils.js
index 2f00b664..ad744f53 100644
--- a/src/mainPage/features/interactions/v2l/utils.js
+++ b/src/mainPage/features/interactions/v2l/utils.js
@@ -47,6 +47,23 @@ class PieceData {
newPiece.rotate = this.rotate % 4;
return newPiece;
}
+ getLabel() {
+ if (this.type === LINEAR) {
+ if (this.rotate % 2) return "위에서 아래로 이어짐.";
+ else return "왼쪽에서 오른쪽으로 이어짐.";
+ } else if (this.type === CURVED) {
+ switch (this.rotate % 4) {
+ case 0:
+ return "오른쪽에서 아래로 이어짐.";
+ case 1:
+ return "왼쪽에서 아래로 이어짐.";
+ case 2:
+ return "왼쪽에서 위로 이어짐.";
+ case 3:
+ return "오른쪽에서 위로 이어짐.";
+ }
+ } else return "알 수 없는 모양.";
+ }
}
export function generatePiece(shapeString) {
diff --git a/src/mainPage/features/qna/QnAArticle.jsx b/src/mainPage/features/qna/QnAArticle.jsx
index ae9ba551..86c8ea58 100644
--- a/src/mainPage/features/qna/QnAArticle.jsx
+++ b/src/mainPage/features/qna/QnAArticle.jsx
@@ -24,7 +24,7 @@ function QnAArticle({ question, answer }) {
return (
-
+
+
diff --git a/src/mainPage/shared/auth/AuthButton.jsx b/src/mainPage/shared/auth/AuthButton.jsx
new file mode 100644
index 00000000..846a5273
--- /dev/null
+++ b/src/mainPage/shared/auth/AuthButton.jsx
@@ -0,0 +1,43 @@
+import openModal from "@common/modal/openModal.js";
+import AuthModal from "./AuthModal.jsx";
+import WelcomeModal from "./Welcome";
+import LogoutModal from "./Logout/LogoutConfirmModal.jsx";
+import useAuthStore from "./store.js";
+import useDrawEventStore from "@main/drawEvent/store.js";
+
+function AuthButton() {
+ const isLogin = useAuthStore((store) => store.isLogin);
+ const userName = useAuthStore((store) => store.userName);
+ const setCurrentJoin = useDrawEventStore((store) => store.setCurrentJoin);
+
+ const welcomeModal = ;
+ const authModal = (
+ isFreshMember && openModal(welcomeModal)} />
+ );
+ const logoutModal = setCurrentJoin(false)} />;
+
+ if (isLogin)
+ return (
+
+ );
+
+ return (
+
+ );
+}
+
+export default AuthButton;
diff --git a/src/mainPage/shared/auth/Logout/LogoutAlertModal.jsx b/src/mainPage/shared/auth/Logout/LogoutAlertModal.jsx
new file mode 100644
index 00000000..09cd2ead
--- /dev/null
+++ b/src/mainPage/shared/auth/Logout/LogoutAlertModal.jsx
@@ -0,0 +1,20 @@
+import { useContext } from "react";
+import { ModalCloseContext } from "@common/modal/modal.jsx";
+import AlertModalContainer from "@main/components/AlertModalContainer.jsx";
+import Button from "@common/components/Button.jsx";
+
+function LogoutAlertModal() {
+ const close = useContext(ModalCloseContext);
+
+ return (
+
+
+
+
+
+ );
+}
+
+export default LogoutAlertModal;
diff --git a/src/mainPage/shared/auth/Logout/LogoutConfirmModal.jsx b/src/mainPage/shared/auth/Logout/LogoutConfirmModal.jsx
new file mode 100644
index 00000000..a5de5b2c
--- /dev/null
+++ b/src/mainPage/shared/auth/Logout/LogoutConfirmModal.jsx
@@ -0,0 +1,32 @@
+import { useContext } from "react";
+import { ModalCloseContext } from "@common/modal/modal.jsx";
+import openModal from "@common/modal/openModal.js";
+import AlertModalContainer from "@main/components/AlertModalContainer.jsx";
+import Button from "@common/components/Button.jsx";
+
+import { logout } from "@main/auth/store.js";
+import LogoutAlertModal from "./LogoutAlertModal.jsx";
+
+function LogoutConfirmModal({ onLogout }) {
+ const close = useContext(ModalCloseContext);
+ function clickLogout() {
+ logout();
+ onLogout?.();
+ openModal();
+ }
+
+ return (
+
+
+
+
+
+
+ );
+}
+
+export default LogoutConfirmModal;
diff --git a/src/mainPage/shared/auth/store.js b/src/mainPage/shared/auth/store.js
index 44754b5c..04d6ff24 100644
--- a/src/mainPage/shared/auth/store.js
+++ b/src/mainPage/shared/auth/store.js
@@ -34,18 +34,18 @@ function createUserStore() {
if (typeof window === "undefined") return defaultUserState;
tokenSaver.init(SERVICE_TOKEN_ID);
const token = tokenSaver.get(SERVICE_TOKEN_ID);
- const userName = parseTokenToUserName(token);
- if (token === null) return { isLogin: false, userName: "" };
- else return { isLogin: true, userName };
+ const { userName, userId } = parseTokenAndGetData(token);
+ if (token === null) return { isLogin: false, userName: "", userId: "" };
+ else return { isLogin: true, userName, userId };
}
-function parseTokenToUserName(token) {
+function parseTokenAndGetData(token) {
if (token === null) return "";
try {
- const { userName } = jwtDecode(token);
- return userName;
+ const { userName, userId } = jwtDecode(token);
+ return { userName, userId };
} catch {
- return "사용자";
+ return { userName: "사용자", userId: "1nvalidU5er" };
}
}
@@ -53,16 +53,16 @@ const userStore = new UserStore();
export function login(token) {
tokenSaver.set(token);
- const userName = parseTokenToUserName(token);
- userStore.setState(() => ({ isLogin: true, userName }));
+ const { userName, userId } = parseTokenAndGetData(token);
+ userStore.setState(() => ({ isLogin: true, userName, userId }));
}
export function logout() {
tokenSaver.remove();
- userStore.setState(() => ({ isLogin: false, userName: "" }));
+ userStore.setState(() => ({ isLogin: false, userName: "", userId: "" }));
}
-function useUserStore(func, defaultValue = defaultUserState) {
+function useAuthStore(func, defaultValue = defaultUserState) {
return useSyncExternalStore(
userStore.subscribe.bind(userStore),
() => userStore.getState(func),
@@ -74,4 +74,4 @@ export function isLogined() {
return userStore.state.isLogin;
}
-export default useUserStore;
+export default useAuthStore;
diff --git a/src/mainPage/shared/components/AlertModalContainer.jsx b/src/mainPage/shared/components/AlertModalContainer.jsx
new file mode 100644
index 00000000..c009d92d
--- /dev/null
+++ b/src/mainPage/shared/components/AlertModalContainer.jsx
@@ -0,0 +1,24 @@
+function AlertModalContainer({ title, description, image, children }) {
+ const containerStyle =
+ "w-[calc(100%-1rem)] max-w-[31.25rem] h-[calc(100svh-2rem)] p-10 shadow bg-white relative flex flex-col justify-between items-center";
+ return (
+
+
+
{title}
+
+ {description}
+
+
+ {image && (
+
+ {image}
+
+ )}
+ {children}
+
+ );
+}
+
+export default AlertModalContainer;
diff --git a/src/mainPage/shared/components/NoServerModal.jsx b/src/mainPage/shared/components/NoServerModal.jsx
index 4b9e62e3..88254fc9 100644
--- a/src/mainPage/shared/components/NoServerModal.jsx
+++ b/src/mainPage/shared/components/NoServerModal.jsx
@@ -1,25 +1,21 @@
import { useContext } from "react";
import { ModalCloseContext } from "@common/modal/modal.jsx";
+import AlertModalContainer from "@main/components/AlertModalContainer.jsx";
import Button from "@common/components/Button.jsx";
function NoServerModal() {
const close = useContext(ModalCloseContext);
return (
-
-
-
서버가 닫혔어요!
-
- 괜찮아요. 저희는 서버가 닫혀도 일부 동작은 가능하니까요!
-
-
-
-
-
+
}
+ >
-
+
);
}
diff --git a/src/mainPage/shared/components/ResetButton.jsx b/src/mainPage/shared/components/ResetButton.jsx
index d7ef84e4..e4a24693 100644
--- a/src/mainPage/shared/components/ResetButton.jsx
+++ b/src/mainPage/shared/components/ResetButton.jsx
@@ -1,7 +1,7 @@
import Button from "@common/components/Button.jsx";
import RefreshIcon from "./refresh.svg?react";
-export default function ResetButton({ onClick }) {
+export default function ResetButton({ onClick, disabled }) {
return (
diff --git a/src/mainPage/shared/drawEvent/DrawEventFetcher.jsx b/src/mainPage/shared/drawEvent/DrawEventFetcher.jsx
index 6eb3414b..3300919f 100644
--- a/src/mainPage/shared/drawEvent/DrawEventFetcher.jsx
+++ b/src/mainPage/shared/drawEvent/DrawEventFetcher.jsx
@@ -1,10 +1,11 @@
-import useUserStore from "@main/auth/store.js";
+import useAuthStore from "@main/auth/store.js";
import useDrawStore from "./store.js";
function InteractionEventJoinDataFetcher() {
- const isLogin = useUserStore((store) => store.isLogin);
+ const userId = useAuthStore((store) => store.userId);
const getData = useDrawStore((store) => store.getJoinData);
- getData(isLogin);
+
+ getData(userId);
return null;
}
diff --git a/src/mainPage/shared/drawEvent/store.js b/src/mainPage/shared/drawEvent/store.js
index 35d444cb..1f60d271 100644
--- a/src/mainPage/shared/drawEvent/store.js
+++ b/src/mainPage/shared/drawEvent/store.js
@@ -20,37 +20,48 @@ const drawEventStore = create((set, get) => ({
openBaseDate: new Date("9999-12-31"),
currentJoined: false,
fallbackMode: false,
- getJoinData: (logined) => {
+ getJoinData: (userId) => {
async function promiseFn() {
try {
const [serverTime, joinStatus] = await Promise.all([
getQuery("server-time", getServerPresiseTime),
getJoinDataEvent(),
]);
- const currentDay = getDayDifference(EVENT_START_DATE, serverTime);
-
- let currentJoined = get().currentJoined;
- if (!currentJoined && currentDay >= 0 && currentDay < joinStatus.length) {
- currentJoined = joinStatus[currentDay];
- }
-
- set({ joinStatus, openBaseDate: serverTime, currentJoined, fallbackMode: false });
- return joinStatus;
- } catch {
- set({
+ return { joinStatus, openBaseDate: serverTime, fallbackMode: false };
+ } catch (e) {
+ return {
joinStatus: [false, false, false, false, false],
openBaseDate: new Date("9999-12-31"),
currentJoined: false,
fallbackMode: true,
- });
- return [false, false, false, false, false];
+ };
}
}
- return getQuerySuspense(`draw-info-data@${logined}`, promiseFn, [set]);
+ async function setter() {
+ const newState = await getQuery(`draw-info-data@${userId}`, promiseFn);
+ set(newState);
+ return newState;
+ }
+ return getQuerySuspense("__zustand__draw-event-store-getData", setter, [userId, set]);
},
setCurrentJoin: (value) => {
set({ currentJoined: value });
},
+ readjustJoinStatus: (index) => {
+ set(({ joinStatus }) => {
+ const newJoinStatus = [...joinStatus];
+ newJoinStatus[index] = true;
+ return { joinStatus: newJoinStatus };
+ });
+ },
+ setFallbackMode: () => {
+ set({
+ joinStatus: [false, false, false, false, false],
+ openBaseDate: new Date("9999-12-31"),
+ currentJoined: false,
+ fallbackMode: true,
+ });
+ },
getJoinStatus: (index) => {
if (get().isTodayEvent(index)) return get().currentJoined || get().joinStatus[index];
return get().joinStatus[index];
diff --git a/src/mainPage/shared/realtimeEvent/store.js b/src/mainPage/shared/realtimeEvent/store.js
index f78dd4dd..f2aa8741 100644
--- a/src/mainPage/shared/realtimeEvent/store.js
+++ b/src/mainPage/shared/realtimeEvent/store.js
@@ -63,21 +63,27 @@ const fcfsStore = create((set) => ({
// get countdown and syncronize state
const countdown = Math.ceil((currentEventTime - currentServerTime) / 1000);
- set({
+ return {
currentServerTime,
currentEventTime,
countdown,
eventStatus: eventInfo.eventStatus,
- });
+ };
};
- return getQuerySuspense("fcfs-info-data", promiseFn, [set]);
+ const setter = async function () {
+ const newState = await getQuery("fcfs-info-data", promiseFn);
+ set(newState);
+ return newState;
+ };
+ return getQuerySuspense("fcfs-info-data", setter, [set]);
},
- getPariticipatedData: (isLogin) => {
- const promiseFn = async function () {
- const participated = await getFcfsParticipated();
+ getPariticipatedData: (userId) => {
+ const setter = async function () {
+ const participated = await getQuery(`fcfs-participated-data@${userId}`, getFcfsParticipated);
set({ isParticipated: participated });
+ return participated;
};
- return getQuerySuspense(`fcfs-participated-data@${isLogin}`, promiseFn, [set]);
+ return getQuerySuspense(`__zustand__fcfs-participated-getData`, setter, [set, userId]);
},
setEventStatus: (eventStatus) => set({ eventStatus }),
handleCountdown: () =>