Skip to content

Commit

Permalink
Merge pull request #39 from rlarlduf20/#38-addMate
Browse files Browse the repository at this point in the history
#38 add mate
  • Loading branch information
rlarlduf20 authored Jan 17, 2024
2 parents a9b6239 + 3818384 commit cfe5063
Show file tree
Hide file tree
Showing 23 changed files with 465 additions and 9 deletions.
13 changes: 13 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"autoprefixer": "10.4.16",
"eslint": "8.49.0",
"eslint-config-next": "13.5.2",
"event-source-polyfill": "^1.0.31",
"jsonwebtoken": "^9.0.2",
"konva": "^9.2.3",
"next": "^13.5.4",
Expand All @@ -33,6 +34,7 @@
"ws": "^8.14.2"
},
"devDependencies": {
"@types/event-source-polyfill": "^1.0.5",
"@types/jsonwebtoken": "^9.0.3",
"@types/sockjs-client": "^1.5.4",
"@types/ws": "^8.5.10"
Expand Down
File renamed without changes.
File renamed without changes.
12 changes: 12 additions & 0 deletions src/app/(main)/@addMateModal/(.)addMate/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import Modal from "@/components/Modal";
import AddMateSection from "@/templates/AddMateSection";

const AddMateModalPage = () => {
return (
<Modal>
<AddMateSection />
</Modal>
);
};

export default AddMateModalPage;
5 changes: 5 additions & 0 deletions src/app/(main)/@addMateModal/default.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const Default = () => {
return null;
};

export default Default;
11 changes: 11 additions & 0 deletions src/app/(main)/addMate/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import AddMateSection from "@/templates/AddMateSection";

const AddMatePage = () => {
return (
<section className="pt-[120px] flex items-center justify-center">
<AddMateSection />
</section>
);
};

export default AddMatePage;
12 changes: 9 additions & 3 deletions src/app/(main)/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,24 @@ export const metadata: Metadata = {

interface RootLayoutProps {
children: React.ReactNode;
modal: React.ReactNode;
addAlbumModal: React.ReactNode;
addMateModal: React.ReactNode;
}

const RootLayout = async ({ children, modal }: RootLayoutProps) => {
const RootLayout = async ({
children,
addAlbumModal,
addMateModal,
}: RootLayoutProps) => {
return (
<html lang="en" className={`${SUITFont.variable}`}>
<body>
<AuthSessionProvider>
<Header />
<main className="w-inner mx-auto">
{children}
{modal}
{addAlbumModal}
{addMateModal}
</main>
<Footer />
</AuthSessionProvider>
Expand Down
20 changes: 20 additions & 0 deletions src/app/api/inviteUser/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { getServerSession } from "next-auth";
import { authOptions } from "../auth/[...nextauth]/route";

export async function POST(request: Request) {
const { jogakTokens } = await getServerSession(authOptions);
const { userID } = await request.json();

const res = await fetch(
`${process.env.SERVER_URL}/user/friend?socialID=${userID}`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${jogakTokens.accessToken}`,
},
}
);

return res;
}
21 changes: 21 additions & 0 deletions src/app/api/replyMate/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { getServerSession } from "next-auth";
import { authOptions } from "../auth/[...nextauth]/route";

export async function POST(request: Request) {
const { jogakTokens } = await getServerSession(authOptions);

const { responseType, userID } = await request.json();

const res = await fetch(
`${process.env.SERVER_URL}/user/friend-reply?socialID=${userID}&reply=${responseType}`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${jogakTokens.accessToken}`,
},
}
);

return res;
}
19 changes: 19 additions & 0 deletions src/app/api/searchUser/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { getServerSession } from "next-auth";
import { authOptions } from "../auth/[...nextauth]/route";

export async function POST(request: Request) {
const { jogakTokens } = await getServerSession(authOptions);

const { debouncedSearchText } = await request.json();
const res = await fetch(
`${process.env.SERVER_URL}/user/search?nickname=${debouncedSearchText}`,
{
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${jogakTokens.accessToken}`,
},
}
);

return res;
}
2 changes: 1 addition & 1 deletion src/components/Modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const Modal = ({ children }: ModalProps) => {
const router = useRouter();
const pathname = usePathname();

if (!pathname.includes("addAlbum")) {
if (!pathname.includes("addAlbum") && !pathname.includes("addMate")) {
return null;
}
const handleOnOpenChange = (open: boolean) => {
Expand Down
145 changes: 145 additions & 0 deletions src/components/Notification.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
"use client";

import useMouseDownOutside from "@/hooks/useMouseDownOutside";
import usePushNotification from "@/hooks/usePushNotification";
import { useEffect, useRef, useState } from "react";
import type { FriendsType } from "@/types";
import { useSession } from "next-auth/react";

interface PushNotiPropsType {
info: FriendsType | any;
handleResponse: (r: string, u: string, n: string) => void;
setIsAppear?: any;
handleFilterPushMsg: (u: string) => void;
}

const PushNoti = ({
info,
handleResponse,
setIsAppear,
handleFilterPushMsg,
}: PushNotiPropsType) => {
return (
<div className="p-2 border-b-2 border-white">
<p className="pb-3">{info?.nickname}님이 친구 요청을 보냈습니다.</p>
<div>
<button
onClick={() => {
handleResponse("reject", info.socialID, info.nickname);
handleFilterPushMsg(info.socialID);
setIsAppear(false);
}}
className="border-[1px] border-white px-3 py-1"
>
거절
</button>
<button
onClick={() => {
handleResponse("accept", info.socialID, info.nickname);
handleFilterPushMsg(info.socialID);
setIsAppear(false);
}}
className="bg-white border-[1px] text-main_black px-3 py-1"
>
수락
</button>
</div>
</div>
);
};

const Notification = () => {
const notificationRef = useRef<HTMLDivElement>(null);
const { isOpen, setIsOpen } = useMouseDownOutside(notificationRef);
const { pushMsg, isAppear, setIsAppear } = usePushNotification();
const [receivedReq, setReceivedReq] = useState<FriendsType[]>([]);
const { data: session } = useSession();

const handleResponse = async (
responseType: string,
userID: string,
nickname: string
) => {
const res = await fetch("api/replyMate", {
method: "POST",
body: JSON.stringify({
userID,
responseType,
}),
});
if (res.ok) {
if (responseType === "reject") alert("거절하셨습니다.");
if (responseType === "accept")
alert(`${nickname}님과 친구가 되었습니다.`);
}
};
const handleFilterPushMsg = (userID: string) => {
const filteredReceivedReq = receivedReq.filter((item) => {
return item.socialID !== userID;
});
setReceivedReq(filteredReceivedReq);
};

useEffect(() => {
const getReceivedReq = async () => {
const res = await fetch(
`${process.env.NEXT_PUBLIC_SERVER_URL}/user/profile`,
{
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${session?.jogakTokens.accessToken}`,
},
}
);
const data = await res.json();
setReceivedReq(data.receivedFriendRequest);
};
if (session?.jogakTokens.accessToken) {
getReceivedReq();
}
if (pushMsg) {
setReceivedReq((prev: any) => {
return [...prev, pushMsg];
});
}
}, [session?.jogakTokens.accessToken, pushMsg]);

return (
<section ref={notificationRef} className="relative">
<div
className="border-[1px] border-white p-3 cursor-pointer flex"
onClick={() => setIsOpen((prev) => !prev)}
>
<p className="grow">알림</p>
<div className="border-[1px] border-white w-[24px] text-center">
{receivedReq.length}
</div>
</div>
{isOpen && (
<div className="absolute w-[200px] h-[400px] border-[1px] border-white bg-main_black">
{receivedReq.map((item, index) => (
<PushNoti
key={index}
info={item}
handleResponse={handleResponse}
setIsAppear={setIsAppear}
handleFilterPushMsg={handleFilterPushMsg}
/>
))}
</div>
)}
{isAppear && (
<div className="fixed top-[50px] left-[300px] bg-main_black z-30">
<PushNoti
info={pushMsg}
handleResponse={handleResponse}
setIsAppear={setIsAppear}
handleFilterPushMsg={handleFilterPushMsg}
/>
</div>
)}
</section>
);
};

export default Notification;
2 changes: 1 addition & 1 deletion src/components/header/LogoText.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const LogoText = () => {

return (
<p className="flex-grow font-semibold text-[22px] ml-[10px]">
{pathname === "/" ? "내 조각보" : "둘러보기"}
{pathname === "/browse" ? "둘러보기" : "내 조각보"}
</p>
);
};
Expand Down
2 changes: 2 additions & 0 deletions src/components/header/MainNav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ import LogoText from "./LogoText";
import NavLink from "./NavLink";
import HomeLogoIcon from "../../../public/images/svg/home-logo.svg";
import RouteTrapezoidIcon from "../../../public/images/svg/route-trapezoid.svg";
import Notification from "../Notification";

const MainNav = () => {
return (
<nav className="flex h-[80px] w-inner items-center">
<Image src={HomeLogoIcon} alt="홈 로고 아이콘" />
<LogoText />
<Notification />
<Image src={RouteTrapezoidIcon} alt="사다리꼴 아이콘" />
<div className="ml-[3px] flex gap-[3px]">
<NavLink />
Expand Down
6 changes: 3 additions & 3 deletions src/components/header/NavLink.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ const NavLink = () => {

return (
<>
{pathname === "/" ? (
<Link href="/browse">둘러보기</Link>
) : (
{pathname === "/browse" ? (
<Link href="/">내 조각보</Link>
) : (
<Link href="/browse">둘러보기</Link>
)}
</>
);
Expand Down
17 changes: 17 additions & 0 deletions src/hooks/useDebounce.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { useState, useEffect } from "react";

const useDebounce = (value: string, delay: number) => {
const [debouncedValue, setDebouncedValue] = useState<string>(value);

useEffect(() => {
const timer = setTimeout(() => {
setDebouncedValue(value);
}, delay);

return () => clearTimeout(timer);
}, [value, delay]);

return debouncedValue;
};

export default useDebounce;
Loading

0 comments on commit cfe5063

Please sign in to comment.