Skip to content

Commit

Permalink
feat: 공유하기 구현
Browse files Browse the repository at this point in the history
카카오 제외 나머지 구현 완료
  • Loading branch information
j2an777 committed Jun 23, 2024
1 parent 555395a commit 5419f76
Show file tree
Hide file tree
Showing 9 changed files with 314 additions and 4 deletions.
2 changes: 2 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>D'art</title>
<!-- Kakao share api init -->
<script src="https://t1.kakaocdn.net/kakao_js_sdk/2.7.2/kakao.min.js" integrity="sha384-TiCUE00h649CAMonG018J2ujOgDKW/kVWlChEuu4jK2vxfAAD0eZxzCKakxg55G4" crossorigin="anonymous"></script>
</head>
<body>
<div id="alert-portal"></div>
Expand Down
6 changes: 6 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@ import { Navbar } from '@/components';
import { routes } from './routes';
import ProtectedRoute from './routes/ProtectedRoute';

declare global {
interface Window {
Kakao: any;
}
}

function App() {
return (
<Routes>
Expand Down
34 changes: 33 additions & 1 deletion src/components/icon/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,10 @@ export type IconValues =
| 'template2'
| 'template3'
| 'template4'
| 'watch';
| 'watch'
| 'share'
| 'kakaoShare'
| 'naver';

interface IconProps {
value: IconValues;
Expand Down Expand Up @@ -851,6 +854,35 @@ const renderIcon = (
</defs>
</svg>
);
case 'share':
return (
<svg width="200" height="200" viewBox="0 0 200 200" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M74.9997 99.9998C74.9997 105.525 72.8047 110.824 68.8977 114.731C64.9907 118.638 59.6917 120.833 54.1663 120.833C48.641 120.833 43.342 118.638 39.4349 114.731C35.5279 110.824 33.333 105.525 33.333 99.9998C33.333 94.4745 35.5279 89.1755 39.4349 85.2684C43.342 81.3614 48.641 79.1665 54.1663 79.1665C59.6917 79.1665 64.9907 81.3614 68.8977 85.2684C72.8047 89.1755 74.9997 94.4745 74.9997 99.9998Z" stroke="white" strokeWidth="12.5"/>
<path d="M116.667 54.1665L75 83.3332M116.667 145.833L75 116.667" stroke="white" strokeWidth="12.5" strokeLinecap="round"/>
<path d="M158.333 154.167C158.333 159.692 156.138 164.991 152.231 168.898C148.324 172.805 143.025 175 137.499 175C131.974 175 126.675 172.805 122.768 168.898C118.861 164.991 116.666 159.692 116.666 154.167C116.666 148.641 118.861 143.342 122.768 139.435C126.675 135.528 131.974 133.333 137.499 133.333C143.025 133.333 148.324 135.528 152.231 139.435C156.138 143.342 158.333 148.641 158.333 154.167ZM158.333 45.8333C158.333 51.3587 156.138 56.6577 152.231 60.5647C148.324 64.4717 143.025 66.6667 137.499 66.6667C131.974 66.6667 126.675 64.4717 122.768 60.5647C118.861 56.6577 116.666 51.3587 116.666 45.8333C116.666 40.308 118.861 35.0089 122.768 31.1019C126.675 27.1949 131.974 25 137.499 25C143.025 25 148.324 27.1949 152.231 31.1019C156.138 35.0089 158.333 40.308 158.333 45.8333Z" stroke="white" strokeWidth="12.5"/>
</svg>
);
case 'kakaoShare':
return (
<svg width="100" height="100" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="50" cy="50" r="50" fill="#FFDE00"/>
<path d="M49.9996 20C69.3329 20 85.0029 32.2133 85.0029 47.2833C85.0029 62.35 69.3329 74.5633 50.0029 74.5633C48.0783 74.5599 46.1558 74.4375 44.2463 74.1967L29.5529 83.8067C27.8829 84.69 27.2929 84.5933 27.9796 82.43L30.9529 70.17C21.3529 65.3033 15.0029 56.87 15.0029 47.2833C15.0029 32.2167 30.6696 20 50.0029 20M69.6963 46.8667L74.5963 42.12C74.879 41.8264 75.0367 41.4344 75.0362 41.0268C75.0358 40.6192 74.8771 40.2277 74.5936 39.9347C74.3102 39.6417 73.9241 39.4702 73.5167 39.4563C73.1093 39.4424 72.7124 39.5871 72.4096 39.86L65.9829 46.08V40.94C65.9829 40.5227 65.8172 40.1225 65.5221 39.8275C65.227 39.5324 64.8269 39.3667 64.4096 39.3667C63.9923 39.3667 63.5921 39.5324 63.2971 39.8275C63.002 40.1225 62.8363 40.5227 62.8363 40.94V49.4633C62.7808 49.7069 62.7808 49.9598 62.8363 50.2033V55C62.8363 55.4173 63.002 55.8175 63.2971 56.1125C63.5921 56.4076 63.9923 56.5733 64.4096 56.5733C64.8269 56.5733 65.227 56.4076 65.5221 56.1125C65.8172 55.8175 65.9829 55.4173 65.9829 55V50.4567L67.4063 49.08L72.1663 55.8567C72.2851 56.0259 72.4361 56.17 72.6107 56.2808C72.7852 56.3916 72.9799 56.467 73.1835 56.5026C73.3872 56.5382 73.5959 56.5333 73.7977 56.4883C73.9995 56.4432 74.1904 56.3588 74.3596 56.24C74.5288 56.1212 74.6729 55.9701 74.7837 55.7956C74.8946 55.6211 74.9699 55.4264 75.0055 55.2227C75.0411 55.0191 75.0362 54.8104 74.9912 54.6086C74.9461 54.4068 74.8618 54.2159 74.7429 54.0467L69.6963 46.8667ZM59.8363 53.28H54.9696V40.99C54.951 40.5857 54.7772 40.2041 54.4845 39.9246C54.1918 39.645 53.8027 39.4891 53.3979 39.4891C52.9932 39.4891 52.604 39.645 52.3113 39.9246C52.0186 40.2041 51.8449 40.5857 51.8263 40.99V54.8533C51.8263 55.72 52.5263 56.4267 53.3963 56.4267H59.8363C60.2535 56.4267 60.6537 56.2609 60.9488 55.9659C61.2438 55.6708 61.4096 55.2706 61.4096 54.8533C61.4096 54.4361 61.2438 54.0359 60.9488 53.7408C60.6537 53.4458 60.2535 53.28 59.8363 53.28ZM40.3129 49.6433L42.6329 43.95L44.7596 49.64L40.3129 49.6433ZM48.7229 51.2667L48.7296 51.2133C48.7285 50.817 48.5772 50.4359 48.3063 50.1467L44.8196 40.8133C44.6734 40.3686 44.395 39.979 44.0216 39.6966C43.6481 39.4143 43.1974 39.2527 42.7296 39.2333C42.2588 39.2331 41.799 39.3753 41.4105 39.6412C41.022 39.9072 40.7231 40.2844 40.5529 40.7233L35.0129 54.3067C34.8551 54.693 34.8573 55.1262 35.0188 55.511C35.1804 55.8957 35.4883 56.2005 35.8746 56.3583C36.2609 56.5161 36.6941 56.514 37.0789 56.3524C37.4636 56.1908 37.7685 55.883 37.9263 55.4967L39.0329 52.7867H45.9329L46.9263 55.4533C46.994 55.6527 47.1011 55.8364 47.2411 55.9936C47.3812 56.1507 47.5514 56.2782 47.7416 56.3685C47.9318 56.4587 48.1382 56.5098 48.3485 56.5188C48.5589 56.5278 48.7689 56.4946 48.9661 56.4209C49.1633 56.3473 49.3438 56.2348 49.4968 56.0902C49.6498 55.9455 49.7722 55.7717 49.8567 55.5789C49.9413 55.3861 49.9863 55.1783 49.9891 54.9677C49.9919 54.7572 49.9524 54.5483 49.8729 54.3533L48.7229 51.2667ZM37.6463 41.0067C37.6471 40.8001 37.6072 40.5954 37.5286 40.4043C37.4501 40.2132 37.3345 40.0396 37.1886 39.8934C37.0427 39.7471 36.8693 39.6312 36.6784 39.5523C36.4875 39.4733 36.2828 39.4329 36.0763 39.4333H25.2596C24.8423 39.4333 24.4421 39.5991 24.1471 39.8942C23.852 40.1892 23.6863 40.5894 23.6863 41.0067C23.6863 41.4239 23.852 41.8241 24.1471 42.1192C24.4421 42.4142 24.8423 42.58 25.2596 42.58H29.1263V55.0333C29.1263 55.4506 29.292 55.8508 29.5871 56.1459C29.8821 56.4409 30.2823 56.6067 30.6996 56.6067C31.1169 56.6067 31.5171 56.4409 31.8121 56.1459C32.1072 55.8508 32.2729 55.4506 32.2729 55.0333V42.58H36.0729C36.2798 42.5809 36.4848 42.5408 36.6761 42.462C36.8673 42.3833 37.0411 42.2674 37.1874 42.1212C37.3337 41.9749 37.4495 41.8011 37.5283 41.6098C37.6071 41.4185 37.6471 41.2135 37.6463 41.0067Z" fill="black"/>
</svg>
);
case 'naver':
return (
<svg width="300" height="300" viewBox="0 0 300 300" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="300" height="300" rx="150" fill="#00C13A"/>
<g clip-path="url(#clip0_802_21)">
<path d="M176.706 155.281L121.1 75H75V225H123.287V144.725L178.9 225H225V75H176.706V155.281Z" fill="white"/>
</g>
<defs>
<clipPath id="clip0_802_21">
<rect width="150" height="150" fill="white" transform="translate(75 75)"/>
</clipPath>
</defs>
</svg>
);
}
};

Expand Down
18 changes: 17 additions & 1 deletion src/pages/gallery/components/galleryHeader/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,23 @@ import { postReview } from '@/apis/review';
import useCustomNavigate from '@/hooks/useCustomNavigate';
import { useStore } from 'zustand';
import { memberStore } from '@/stores/member';
import ShareModal from '../shareModal';

interface GalleryHeaderProps {
galleryId: number;
galleryNick: string;
chatRoomId: number;
title: string;
thumbnail: string;
}

const GalleryHeader = ({ galleryId, galleryNick, chatRoomId }: GalleryHeaderProps) => {
const GalleryHeader = ({ galleryId, galleryNick, chatRoomId, title, thumbnail }: GalleryHeaderProps) => {
const { open, close } = useStore(alertStore);
const openChat = chatStore((state) => state.open);
const navigate = useCustomNavigate();
const queryClient = useQueryClient();
const { auth: { nickname }, accessToken } = memberStore();
const location = window.location.href;

const mutation = useMutation({
mutationKey: ['review'],
Expand Down Expand Up @@ -78,6 +82,17 @@ const GalleryHeader = ({ galleryId, galleryNick, chatRoomId }: GalleryHeaderProp
navigate('/');
},
});
} else if (name === 'share') {
open({
title: '전시 공유하기',
description: (
<ShareModal location={location} title={title} thumbnail={thumbnail}/>
),
buttonLabel: '닫기',
onClickButton: () => {
close();
},
})
} else {
open({
title: '전시관 나가기',
Expand All @@ -104,6 +119,7 @@ const GalleryHeader = ({ galleryId, galleryNick, chatRoomId }: GalleryHeaderProp
strokeColor="white"
/>
: null}
<Icon value="share" size={30} onClick={() => onHandleToggle('share')} />
<Icon value="chat" size={30} onClick={() => onHandleToggle('chat')} />
<Icon value="out" size={30} onClick={() => onHandleToggle('out')} />
</S.MenuBox>
Expand Down
3 changes: 2 additions & 1 deletion src/pages/gallery/components/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export { default as GalleryHeader } from './galleryHeader';
export { default as GalleryRotate } from './galleryTemplate/rotate';
export { default as ReviewModal} from './reviewModal';
export { default as ReviewModal } from './reviewModal';
export { default as ShareModal } from './shareModal';
106 changes: 106 additions & 0 deletions src/pages/gallery/components/shareModal/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import { useState } from 'react';
import * as S from './styles';
import { Icon } from '@/components';
import {
EmailShareButton,
EmailIcon,
FacebookShareButton,
FacebookIcon,
TwitterShareButton,
TwitterIcon,
InstapaperIcon,
InstapaperShareButton,
PinterestShareButton,
PinterestIcon,
LineShareButton,
LineIcon,
LinkedinShareButton,
LinkedinIcon,
WhatsappShareButton,
WhatsappIcon,
TumblrShareButton,
TumblrIcon,
TelegramShareButton,
TelegramIcon
} from 'react-share';
import shareKakao from '../../hooks/shareKakao';

interface ShareModalProps {
location: string;
title: string;
thumbnail: string;
}

const ShareModal = ({ location, title, thumbnail }: ShareModalProps) => {
const [copy, setCopy] = useState(false);

const onHandleCopy = () => {
const input = document.querySelector('#share-url') as HTMLInputElement;
input.select();
document.execCommand('copy');
setCopy(true);
};

const onHandleSocial = (platform: string, location?: string, title?: string) => {
if (platform === 'kakao') {
shareKakao();
} else if (platform === 'naver') {
window.open("https://share.naver.com/web/shareView?url=" + location + "&title=" + title);
}
};

return (
<S.Container>
<S.UrlBox>
<S.UrlText
readOnly
value={location}
id='share-url'/>
<S.CopyBtn onClick={onHandleCopy}>URL 복사</S.CopyBtn>
<Icon value='check' size={20} color={copy ? 'black' : 'gray400'}/>
</S.UrlBox>
<S.SocialBox>
<EmailShareButton url={location}>
<EmailIcon size={48} round={true} borderRadius={24}></EmailIcon>
</EmailShareButton>
<S.SocialBtn
onClick={() => onHandleSocial('kakao')}
id="kakao-sharing-btn">
<Icon value='kakaoShare' size={50} $active={false}/>
</S.SocialBtn>
<S.SocialBtn onClick={() => onHandleSocial('naver', location, title)}>
<Icon value='naver' size={50} $active={false} />
</S.SocialBtn>
<FacebookShareButton url={location}>
<FacebookIcon size={48} round={true} borderRadius={24}></FacebookIcon>
</FacebookShareButton>
<TwitterShareButton url={location}>
<TwitterIcon size={48} round={true} borderRadius={24}></TwitterIcon>
</TwitterShareButton>
<LineShareButton url={location}>
<LineIcon size={48} round={true} borderRadius={24}></LineIcon>
</LineShareButton>
<InstapaperShareButton url={location}>
<InstapaperIcon size={48} round={true} borderRadius={24}></InstapaperIcon>
</InstapaperShareButton>
<PinterestShareButton url={location} media={thumbnail}>
<PinterestIcon size={48} round={true} borderRadius={24}></PinterestIcon>
</PinterestShareButton>
<LinkedinShareButton url={location}>
<LinkedinIcon size={48} round={true} borderRadius={24}></LinkedinIcon>
</LinkedinShareButton>
<WhatsappShareButton url={location}>
<WhatsappIcon size={48} round={true} borderRadius={24}></WhatsappIcon>
</WhatsappShareButton>
<TumblrShareButton url={location}>
<TumblrIcon size={48} round={true} borderRadius={24}></TumblrIcon>
</TumblrShareButton>
<TelegramShareButton url={location}>
<TelegramIcon size={48} round={true} borderRadius={24}></TelegramIcon>
</TelegramShareButton>
</S.SocialBox>
</S.Container>
)
}

export default ShareModal
67 changes: 67 additions & 0 deletions src/pages/gallery/components/shareModal/styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { buttonSizeMap, buttonTypeMap } from "@/styles/button";
import { colors } from "@/styles/colorPalette";
import { LayoutMap } from "@/styles/layout";
import { typographyMap } from "@/styles/typography";
import styled from "@emotion/styled";

export const Container = styled.div`
${LayoutMap.displayFlex};
flex-direction: column;
gap : 30px;
padding : 20px;
`;

export const UrlBox = styled.div`
width : 400px;
height : 50px;
background-color : ${colors.gray100};
${LayoutMap.displayFlex};
justify-content : center;
gap : 20px;
padding : 10px;
border-radius : 10px;
`;

export const UrlText = styled.input`
width : 60%;
height: 100%;
background-color : white;
${LayoutMap.displayFlex};
padding-left : 5px;
white-space: nowrap;
overflow : hidden;
text-overflow : ellipsis;
cursor: text;
border-radius : 5px;
`;

export const CopyBtn = styled.button`
width : 55%;
height : 100%;
${buttonTypeMap.rectangleWhite};
${buttonSizeMap.fit};
${typographyMap.t7};
padding : 5px;
transition : all 0.3s ease;
border-radius : 5px;
&:hover {
${buttonTypeMap.rectangleGray};
}
`;

export const SocialBox = styled.div`
width : 100%;
height : 100px;
display: grid;
grid-template-columns: repeat(auto-fill, minmax(50px, 1fr));
grid-gap: 10px;
justify-items: center;
align-items: center;
margin-bottom : 10px;
`;

export const SocialBtn = styled.button`
width : 50px;
height : 50px;
`;
80 changes: 80 additions & 0 deletions src/pages/gallery/hooks/shareKakao.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@



export const shareKakao = () => {

if (window.Kakao) {
const kakao = window.Kakao;

if (!kakao.isInitialized()) {
kakao.init('2de91a34726841d2d47e292877371dbd');
}

kakao.Share.sendDefault({
objectType: 'feed',
content: {
title: '안녕하세요',
description: '아메리카노, 빵, 케익',
imageUrl: 'https://mud-kage.kakao.com/dn/NTmhS/btqfEUdFAUf/FjKzkZsnoeE4o19klTOVI1/openlink_640x640s.jpg',
link: {
mobileWebUrl: 'https://developers.kakao.com',
webUrl: 'https://developers.kakao.com',
},
},
itemContent: {
profileText: 'Kakao',
profileImageUrl: 'https://mud-kage.kakao.com/dn/Q2iNx/btqgeRgV54P/VLdBs9cvyn8BJXB3o7N8UK/kakaolink40_original.png',
titleImageUrl: 'https://mud-kage.kakao.com/dn/Q2iNx/btqgeRgV54P/VLdBs9cvyn8BJXB3o7N8UK/kakaolink40_original.png',
titleImageText: 'Cheese cake',
titleImageCategory: 'Cake',
items: [
{
item: 'Cake1',
itemOp: '1000원',
},
{
item: 'Cake2',
itemOp: '2000원',
},
{
item: 'Cake3',
itemOp: '3000원',
},
{
item: 'Cake4',
itemOp: '4000원',
},
{
item: 'Cake5',
itemOp: '5000원',
},
],
sum: '총 결제금액',
sumOp: '15000원',
},
social: {
likeCount: 10,
commentCount: 20,
sharedCount: 30,
},
buttons: [
{
title: '웹으로 이동',
link: {
mobileWebUrl: 'https://developers.kakao.com',
webUrl: 'https://developers.kakao.com',
},
},
{
title: '앱으로 이동',
link: {
mobileWebUrl: 'https://developers.kakao.com',
webUrl: 'https://developers.kakao.com',
},
},
],
});
}
}

export default shareKakao
2 changes: 1 addition & 1 deletion src/vite-env.d.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
/// <reference types="vite/client" />
/// <reference types="vite/client" />

0 comments on commit 5419f76

Please sign in to comment.