Skip to content

Commit

Permalink
Merge pull request #7 from seondal/develop
Browse files Browse the repository at this point in the history
v0.1.1
  • Loading branch information
seondal authored Nov 24, 2024
2 parents dfd6dd7 + 00ad0cc commit 6af996c
Show file tree
Hide file tree
Showing 12 changed files with 144 additions and 33 deletions.
2 changes: 1 addition & 1 deletion next.config.mjs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
images: {
domains: ["i.ibb.co", "k.kakaocdn.net"],
domains: ["i.ibb.co", "k.kakaocdn.net", "img1.kakaocdn.net"],
},
};

Expand Down
24 changes: 22 additions & 2 deletions src/app/(main)/SearchBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,19 @@ export default function SearchBar() {
const router = useRouter();
const [keyword, setKeyword] = useState<string>("");

function getDisplayedKeyword() {
if (keywordQuery) return keywordQuery;
if (userid) return `@${userid}`;
if (categoryQuery) {
const categoryArray = categoryQuery.split(",").map((item) => `#${item}`);
return categoryArray.join(" ");
}
return "";
}

useEffect(() => {
setKeyword(keywordQuery ?? userid ? `@${userid}` : "");
setKeyword(getDisplayedKeyword());
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [params]);

const [categoryActive, setCategoryActive] = useState(false);
Expand All @@ -32,6 +43,15 @@ export default function SearchBar() {
return router.push(`/?userid=${tmpUserid}`);
}

if (keyword.startsWith("#")) {
const searchedCategoreis = keyword
.split(" ")
.filter((item) => item.startsWith("#"))
.map((item) => item.substring(1));
const categoryQueryString = searchedCategoreis.join(",");
return router.push(`/?category=${categoryQueryString}`);
}

return router.push(`/?keyword=${keyword}`);
}

Expand All @@ -42,7 +62,7 @@ export default function SearchBar() {
<input
name="keyword"
type="search"
placeholder="키워드로 검색하기"
placeholder="키워드,@사용자,#카테고리 검색"
value={keyword}
onChange={(e) => setKeyword(e.target.value)}
/>
Expand Down
5 changes: 4 additions & 1 deletion src/app/api/recipe/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,10 @@ export async function GET(req: NextRequest, res: NextResponse) {
const filteredDocs: DocumentData[] = [];
snapshot.forEach((doc) => {
const data = doc.data();
if (data.description && data.description.includes(keyword)) {
if (
data.title.includes(keyword) ||
(data.description && data.description.includes(keyword))
) {
filteredDocs.push(data);
}
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { NextRequest, NextResponse } from "next/server";
import { kakaoApi } from "../../instance";
import { KAKAO_CLIENT_ID, KAKAO_REDIRECT_URI } from "@/constants/env";
import { kakaoApi } from "../instance";
interface PostKakaoTokenI {
access_token: string;
token_type: string;
Expand Down
23 changes: 19 additions & 4 deletions src/app/auth/signin/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@
import { useEffect } from "react";
import { useRouter } from "next/navigation";
import { myApi } from "@/app/api/instance";
import { auth } from "@/firebase";
import { auth, db } from "@/firebase";
import {
browserLocalPersistence,
OAuthProvider,
setPersistence,
signInWithCredential,
} from "firebase/auth";
import { doc, setDoc } from "firebase/firestore";

interface SigninPageI {
searchParams: { code: string };
Expand All @@ -23,7 +24,7 @@ export default function SigninPage({ searchParams }: SigninPageI) {
const signIn = async () => {
try {
// API 호출
const res = await myApi.get(`/auth/signin`, { params: { code: code } });
const res = await myApi.get(`/signin`, { params: { code: code } });
const data = res.data;

// OAuth provider 설정
Expand All @@ -35,16 +36,30 @@ export default function SigninPage({ searchParams }: SigninPageI) {
// Firebase 인증 설정 및 로그인
await setPersistence(auth, browserLocalPersistence);
const fbRes = await signInWithCredential(auth, credential);
const fbData = fbRes.user;

console.log("🚀 ~ signIn ~ fbData:", fbData);
// 유저 정보 저장
const newRef = doc(db, "user", fbData.uid);
const newDoc = {
name: fbData.displayName,
email: fbData.email,
phoneNumber: fbData.phoneNumber,
photoURL: fbData.photoURL,
providerId: fbData.providerId,
providerData: fbData.providerData,
};
await setDoc(newRef, newDoc);

// 로그인 성공 후 리디렉션
router.replace("/mypage");
alert(`${fbData.displayName}님, 로그인에 성공했어요`);
router.replace("/");
} catch (error) {
console.error("Error during sign-in:", error);
}
};

signIn();
}, [code, router]);

return <div>로그인중...</div>;
}
11 changes: 4 additions & 7 deletions src/app/mypage/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { usePathname, useRouter } from "next/navigation";
import { LayoutI } from "@/interface/page";
import { auth } from "@/firebase";
import Image from "next/image";
import copyToClipboard from "@/utils/copyToClipboard";

const MYPAGE = [
{ text: "업로드한 레시피", path: "/mypage" },
Expand All @@ -17,8 +18,9 @@ export default function MypageLayout({ children }: LayoutI) {
const router = useRouter();

const data = auth.currentUser;
console.log("🚀 ~ MypageLayout ~ data:", data);
async function signIn() {
router.replace(KAKAO_AUTHORIZE);
router.push(KAKAO_AUTHORIZE);
}

function signOut() {
Expand All @@ -28,12 +30,7 @@ export default function MypageLayout({ children }: LayoutI) {

async function handleCopyUserid() {
if (data) {
try {
await navigator.clipboard.writeText(data?.uid);
alert("내 아이디가 복사되었어요");
} catch (error) {
alert("내 아이디 복사에 실패했어요. 다시 시도해주세요");
}
copyToClipboard(data.uid, "내 아이디가 복사되었어요");
}
}

Expand Down
1 change: 1 addition & 0 deletions src/app/mypage/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { auth } from "@/firebase";

export default function Page() {
const data = auth.currentUser;
console.log("🚀 ~ Page ~ data:", data);
const [recipeData, setRecipeData] = useState<RecipeI[]>();

async function fetchFeed() {
Expand Down
18 changes: 16 additions & 2 deletions src/app/upload/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@ export default function UploadPage() {
useState(NO_SELECTION);
const [beforeImage, setBeforeImage] = useState<File>();
const [afterImage, setAfterImage] = useState<File>();
const [loading, setLoading] = useState(false);

const isSubmitActive =
!loading &&
beforeImage !== null &&
afterImage !== null &&
selectedMainCategory !== NO_SELECTION;
Expand All @@ -39,12 +41,17 @@ export default function UploadPage() {
}
}

function handleSubmit() {
setLoading(true);
}

return (
<form
encType="multipart/form-data"
method="POST"
action={`/api/recipe?userid=${data?.uid}`}
onKeyDown={handleKeyDownOnForm}>
onKeyDown={handleKeyDownOnForm}
onSubmit={handleSubmit}>
<label>
*레시피 이름
<input
Expand Down Expand Up @@ -128,7 +135,14 @@ export default function UploadPage() {
<label>
설명 <textarea maxLength={300} name="description" />
</label>
<input type="submit" value="레시피 등록하기" disabled={!isSubmitActive} />

<button
type="submit"
disabled={!isSubmitActive}
aria-busy={loading}
aria-label="업로드 중입니다...">
레시피 등록하기
</button>
</form>
);
}
13 changes: 8 additions & 5 deletions src/components/Category.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ export default function Category({ open, onClose }: CategoryI) {
return searchParam.has({ category: name }) !== undefined;
}

function handleSelectCategory(name: string) {
function handleSelectCategory(e: ChangeEvent<HTMLInputElement>) {
const name = e.target.id;
const param = { category: name };
if (searchParam.has(param)) {
searchParam.del(param);
Expand All @@ -40,21 +41,23 @@ export default function Category({ open, onClose }: CategoryI) {
<div key={main.text} className="flex gap-4 mb-4">
<label className="min-w-fit">
<input
id={main.text}
type="checkbox"
name="main"
onChange={() => handleSelectCategory(main.text)}
defaultChecked={isChecked(main.text)}
onChange={(e) => handleSelectCategory(e)}
checked={isChecked(main.text)}
/>
<strong>{main.text}</strong>
</label>
<div className="flex flex-wrap gap-x-4">
{main.sub.map((sub) => (
<label key={sub}>
<input
id={sub}
type="checkbox"
name="sub"
onClick={() => handleSelectCategory(sub)}
defaultChecked={isChecked(sub)}
onChange={(e) => handleSelectCategory(e)}
checked={isChecked(sub)}
/>
{sub}
</label>
Expand Down
23 changes: 17 additions & 6 deletions src/components/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ import { auth } from "@/firebase";
import {
ArrowUpTrayIcon,
Bars3Icon,
UserCircleIcon,
XMarkIcon,
} from "@heroicons/react/24/outline";
} from "@heroicons/react/24/solid";
import { usePathname, useRouter } from "next/navigation";

export default function Header() {
Expand All @@ -24,7 +25,12 @@ export default function Header() {

return (
<nav className="px-4 items-center">
<h2 style={{ marginBottom: "0px" }}>Filter Recipe</h2>
<h2
style={{ marginBottom: "0px" }}
className="cursor-pointer"
onClick={() => router.push("/")}>
Filter Recipe
</h2>
{isMain ? (
<nav className="gap-4">
<button
Expand All @@ -33,10 +39,15 @@ export default function Header() {
<ArrowUpTrayIcon className="icon-text" />
레시피 등록
</button>
<Bars3Icon
className="icon-button"
onClick={() => router.push("/mypage")}
/>
<div
className="cursor-pointer"
onClick={() => router.push("/mypage")}>
{data ? (
<UserCircleIcon className="icon-button" />
) : (
<Bars3Icon className="icon-button" />
)}
</div>
</nav>
) : (
<XMarkIcon onClick={() => router.back()} className="icon-button" />
Expand Down
19 changes: 15 additions & 4 deletions src/components/RecipeDetailCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import { SITE } from "@/constants/env";
import { ModalI, RecipeCardDataI } from "@/interface/component";
import copyToClipboard from "@/utils/copyToClipboard";
import {
ArrowPathIcon,
EyeIcon,
Expand Down Expand Up @@ -35,8 +36,10 @@ export default function RecipeDetailCard({
const [showRecipe, setShowRecipe] = useState(false);

function handleShare() {
navigator.clipboard.writeText(`${SITE}/recipe/${data.id}`);
alert("레시피를 공유할 수 있는 링크가 클립보드에 복사되었어요!");
copyToClipboard(
`${SITE}/recipe/${data.id}`,
"레시피를 공유할 수 있는 링크가 클립보드에 복사되었어요!"
);
}

function handleClose() {
Expand All @@ -55,6 +58,11 @@ export default function RecipeDetailCard({
handleClose();
}

function handleClickCategory(value: string) {
router.push(`/?category=${value}`);
handleClose();
}

return (
<dialog open={open}>
<article className="">
Expand Down Expand Up @@ -93,8 +101,11 @@ export default function RecipeDetailCard({
@{getShortString(data.userId, 12)}
</a>{" "}
&nbsp;
<b>
#{data.category.main} #{data.category.sub}
<b onClick={() => handleClickCategory(data.category.main)}>
#{data.category.main}
</b>{" "}
<b onClick={() => handleClickCategory(data.category.sub)}>
#{data.category.sub}
</b>
<br />
{data.description}
Expand Down
36 changes: 36 additions & 0 deletions src/utils/copyToClipboard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
export default function copyToClipboard(text: string, successMessage: string) {
if (navigator.clipboard && navigator.clipboard.writeText) {
// Clipboard API 지원 시
navigator.clipboard
.writeText(text)
.then(() => {
alert(successMessage);
})
.catch((err) => {
alert("클립보드 복사에 실패했어요");
console.error("클립보드 복사 실패:", err);
});
} else {
// Clipboard API 미지원 시 폴백
const textarea = document.createElement("textarea");
textarea.value = text;
textarea.style.position = "fixed";
textarea.style.opacity = "0";
document.body.appendChild(textarea);
textarea.focus();
textarea.select();
try {
const successful = document.execCommand("copy");
if (successful) {
alert(successMessage);
} else {
alert("클립보드 복사에 실패했어요");
console.error("텍스트 복사 실패 (Fallback).");
}
} catch (err) {
alert("클립보드 복사에 실패했어요");
console.error("execCommand 복사 실패:", err);
}
document.body.removeChild(textarea);
}
}

0 comments on commit 6af996c

Please sign in to comment.