diff --git a/index.html b/index.html index f1b5528..6a448a2 100644 --- a/index.html +++ b/index.html @@ -7,6 +7,7 @@
+ diff --git a/public/icon/close.svg b/public/icon/close.svg index 5f7e135..b3751f1 100644 --- a/public/icon/close.svg +++ b/public/icon/close.svg @@ -1 +1,4 @@ - \ No newline at end of file + + + + diff --git a/public/icon/kakaotalk.svg b/public/icon/kakaotalk.svg new file mode 100644 index 0000000..ddfa60a --- /dev/null +++ b/public/icon/kakaotalk.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/public/icon/twitter.svg b/public/icon/twitter.svg new file mode 100644 index 0000000..c4081ad --- /dev/null +++ b/public/icon/twitter.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/button/KakaoShareButton.tsx b/src/components/button/KakaoShareButton.tsx new file mode 100644 index 0000000..1422ab3 --- /dev/null +++ b/src/components/button/KakaoShareButton.tsx @@ -0,0 +1,54 @@ +import { useEffect } from "react"; + +interface KakoShareButtonProps { + title: string; + description: string; + imageUrl: string; +} + +const KakaoShareButton = ({ + title, + description, + imageUrl +}: KakoShareButtonProps) => { + const kakaoJavascriptKey = import.meta.env.VITE_KAKAO_JAVASCRIPT_KEY; + + useEffect(() => { + // 카카오톡 SDK 초기화 + if (window.Kakao && !window.Kakao.isInitialized()) { + window.Kakao.init(kakaoJavascriptKey); + } + }, []); + + const handleShare = () => { + if (window.Kakao) { + // 카카오톡 공유 기능 호출 + window.Kakao.Share.sendDefault({ + objectType: "feed", + content: { + title: title, + description: description, + imageUrl: imageUrl, + link: { + mobileWebUrl: window.location.href, + webUrl: window.location.href + } + } + }); + } + }; + return ( +
+ +
+ ); +}; + +export default KakaoShareButton; diff --git a/src/components/button/ShareButton.tsx b/src/components/button/ShareButton.tsx index 04f1998..e461aa3 100644 --- a/src/components/button/ShareButton.tsx +++ b/src/components/button/ShareButton.tsx @@ -1,9 +1,19 @@ +import { useState } from "react"; +import ShareModal from "@/components/modal/ShareModal"; + const ShareButton = () => { - return ( - - ) -} + {shareModalIsOpen && ( + setShareModalIsOpen(false)} /> + )} + + ); +}; -export default ShareButton; \ No newline at end of file +export default ShareButton; diff --git a/src/components/button/TwitterShareButton.tsx b/src/components/button/TwitterShareButton.tsx new file mode 100644 index 0000000..432f2f0 --- /dev/null +++ b/src/components/button/TwitterShareButton.tsx @@ -0,0 +1,15 @@ +const TwitterShareButton = ({ title }: { title: string }) => { + const currentUrl = window.location.href; + + return ( + + 트위터 아이콘 +

트위터

+
+ ); +}; + +export default TwitterShareButton; diff --git a/src/components/button/UrlCopyBar.tsx b/src/components/button/UrlCopyBar.tsx new file mode 100644 index 0000000..f52429d --- /dev/null +++ b/src/components/button/UrlCopyBar.tsx @@ -0,0 +1,14 @@ +import UrlCopyButton from "@/components/button/UrlCopyButton"; + +const UrlCopyBar = () => { + const currentUrl = window.location.href; + + return ( +
+ {currentUrl} + +
+ ); +}; + +export default UrlCopyBar; diff --git a/src/components/button/UrlCopyButton.tsx b/src/components/button/UrlCopyButton.tsx new file mode 100644 index 0000000..f174e38 --- /dev/null +++ b/src/components/button/UrlCopyButton.tsx @@ -0,0 +1,23 @@ +const UrlCopyButton = ({currentUrl} : {currentUrl : string}) => { + const handleCopy = () => { + navigator.clipboard + .writeText(currentUrl) + .then(() => { + alert("URL이 복사되었습니다!"); // toast로 바꾸어야 함 -> 4.10 정준영 + }) + .catch((err) => { + console.error("URL 복사 실패:", err); + }); + }; + + return ( + + ); + }; + + export default UrlCopyButton; \ No newline at end of file diff --git a/src/components/header/Header.tsx b/src/components/header/Header.tsx index d245cc3..26c198c 100644 --- a/src/components/header/Header.tsx +++ b/src/components/header/Header.tsx @@ -14,7 +14,7 @@ type HeaderProps = { const Header = ({ title = "", showPreviousIcon = true, - showShareIcon = false, + showShareIcon = true, children }: HeaderProps) => { const { pathname } = useLocation(); diff --git a/src/components/header/SubHeader.tsx b/src/components/header/SubHeader.tsx index b75239e..124539b 100644 --- a/src/components/header/SubHeader.tsx +++ b/src/components/header/SubHeader.tsx @@ -2,6 +2,7 @@ import { useLocation, useNavigate } from "react-router-dom"; import { useState } from "react"; import useMbtiTestState from "@/store/useMbtiTestState"; import ActionConfirmModal from "@/components/modal/ActionConfirmModal"; +import ShareModal from "@/components/modal/ShareModal"; type SubHeaderProps = { title: string; @@ -12,13 +13,13 @@ type SubHeaderProps = { const SubHeader = ({ title = "", showPreviousIcon = true, - showShareIcon = false + showShareIcon = true }: SubHeaderProps) => { const navigate = useNavigate(); const { pathname, state } = useLocation(); const { currentPage, setPreviousStep } = useMbtiTestState(); const [isLeaveChatModalOpen, setIsLeaveChatModalOpen] = useState(false); - + const [shareModalIsOpen, setShareModalIsOpen] = useState(false); const isProgressPage = pathname === "/mbti-test-progress"; const isChatPage = pathname === "/chat"; const isFirstQuestionPage = currentPage === 1; @@ -50,30 +51,34 @@ const SubHeader = ({ return ( <> -
+
{showPreviousIcon && ( Go To Back )} -

+

{title}

{showShareIcon && ( - Share + )}
@@ -87,6 +92,14 @@ const SubHeader = ({ onConfirm={handleConfirm} /> )} + + {shareModalIsOpen && ( + { + setShareModalIsOpen(false); + }} + /> + )} ); }; diff --git a/src/components/modal/ShareModal.tsx b/src/components/modal/ShareModal.tsx new file mode 100644 index 0000000..ee96d1f --- /dev/null +++ b/src/components/modal/ShareModal.tsx @@ -0,0 +1,63 @@ +import { useEffect, useState } from "react"; +import UrlCopyBar from "@/components/button/UrlCopyBar"; +import TwitterShareButton from "@/components/button/TwitterShareButton"; +import KakaoShareButton from "@/components/button/KakaoShareButton"; + +interface ShareModalProps { + closeModal: () => void; +} + +const ShareModal = ({ closeModal }: ShareModalProps) => { + const [metaData, setMetaData] = useState({ + title: "", + description: "", + imageUrl: "" + }); + + useEffect(() => { + // 메타 데이터를 가져오는 로직 + const title = + document + .querySelector("meta[property='og:title']") + ?.getAttribute("content") || document.title; + const description = + document + .querySelector("meta[property='og:description']") + ?.getAttribute("content") || ""; + const imageUrl = + document + .querySelector("meta[property='og:image']") + ?.getAttribute("content") || ""; + + setMetaData({ title, description, imageUrl }); + }, []); + + return ( +
+
+

+ 게시글 공유 +

+
+ + +
+ +
+ +
+
+
+ ); +}; + +export default ShareModal; diff --git a/src/global.d.ts b/src/global.d.ts new file mode 100644 index 0000000..ec76eb5 --- /dev/null +++ b/src/global.d.ts @@ -0,0 +1,4 @@ +// TypeScript 사용 시에는 TypeScript 가 window객체에 존재하는 Kakao객체를 인식할 수 있도록 src내에 global.d.ts를 설정해줘야 오류가 발생하지 않는다. +interface Window { + Kakao: any; +} \ No newline at end of file diff --git a/src/index.css b/src/index.css index d1e36de..6b64713 100644 --- a/src/index.css +++ b/src/index.css @@ -63,10 +63,6 @@ button { cursor: pointer; } -button:hover { - opacity: 80%; -} - @keyframes pulse-custom { 0%, 100% { diff --git a/src/main.tsx b/src/main.tsx index 12fa35b..2b340b4 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,10 +1,5 @@ -import { StrictMode } from "react"; import { createRoot } from "react-dom/client"; import "./index.css"; import App from "./App"; -createRoot(document.getElementById("root")!).render( - - - -); +createRoot(document.getElementById("root")!).render(); diff --git a/src/types/virtualFreind.ts b/src/types/virtualFreind.ts new file mode 100644 index 0000000..3292655 --- /dev/null +++ b/src/types/virtualFreind.ts @@ -0,0 +1,9 @@ +export interface VirtualFriend { + virtualFriendId: number; + conversationId: number; + mbti: string; + virtualFriendName: string; + virtualFriendAge: number; + virtualFriendSex: string; + virtualFriendRelationship: string; +}