diff --git a/.pnp.cjs b/.pnp.cjs index cc827331..6347cdbf 100755 --- a/.pnp.cjs +++ b/.pnp.cjs @@ -61,6 +61,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["@types/mixpanel-browser", "npm:2.47.1"],\ ["@types/node", "npm:18.15.11"],\ ["@types/react", "npm:18.0.35"],\ + ["@types/react-copy-to-clipboard", "npm:5.0.4"],\ ["@types/react-dom", "npm:18.0.11"],\ ["@types/testing-library__jest-dom", "npm:5.14.8"],\ ["@typescript-eslint/eslint-plugin", "virtual:f05aa4e4719fd537ba4da07d5553d5af64c7f1fdd299ee20cc573ded517ff6989f4f9dcaf607519cf1ea35a7fc8484d64ed69e9ea31b5a59302d59cb203ae9e8#npm:5.62.0"],\ @@ -97,6 +98,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["postcss-syntax", "virtual:f05aa4e4719fd537ba4da07d5553d5af64c7f1fdd299ee20cc573ded517ff6989f4f9dcaf607519cf1ea35a7fc8484d64ed69e9ea31b5a59302d59cb203ae9e8#npm:0.36.2"],\ ["prettier", "npm:2.8.8"],\ ["react", "npm:18.2.0"],\ + ["react-copy-to-clipboard", "virtual:f05aa4e4719fd537ba4da07d5553d5af64c7f1fdd299ee20cc573ded517ff6989f4f9dcaf607519cf1ea35a7fc8484d64ed69e9ea31b5a59302d59cb203ae9e8#npm:5.1.0"],\ ["react-dom", "virtual:f05aa4e4719fd537ba4da07d5553d5af64c7f1fdd299ee20cc573ded517ff6989f4f9dcaf607519cf1ea35a7fc8484d64ed69e9ea31b5a59302d59cb203ae9e8#npm:18.2.0"],\ ["react-hotjar", "npm:6.1.0"],\ ["satori", "npm:0.10.1"],\ @@ -157,6 +159,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["@types/mixpanel-browser", "npm:2.47.1"],\ ["@types/node", "npm:18.15.11"],\ ["@types/react", "npm:18.0.35"],\ + ["@types/react-copy-to-clipboard", "npm:5.0.4"],\ ["@types/react-dom", "npm:18.0.11"],\ ["@types/testing-library__jest-dom", "npm:5.14.8"],\ ["@typescript-eslint/eslint-plugin", "virtual:f05aa4e4719fd537ba4da07d5553d5af64c7f1fdd299ee20cc573ded517ff6989f4f9dcaf607519cf1ea35a7fc8484d64ed69e9ea31b5a59302d59cb203ae9e8#npm:5.62.0"],\ @@ -193,6 +196,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["postcss-syntax", "virtual:f05aa4e4719fd537ba4da07d5553d5af64c7f1fdd299ee20cc573ded517ff6989f4f9dcaf607519cf1ea35a7fc8484d64ed69e9ea31b5a59302d59cb203ae9e8#npm:0.36.2"],\ ["prettier", "npm:2.8.8"],\ ["react", "npm:18.2.0"],\ + ["react-copy-to-clipboard", "virtual:f05aa4e4719fd537ba4da07d5553d5af64c7f1fdd299ee20cc573ded517ff6989f4f9dcaf607519cf1ea35a7fc8484d64ed69e9ea31b5a59302d59cb203ae9e8#npm:5.1.0"],\ ["react-dom", "virtual:f05aa4e4719fd537ba4da07d5553d5af64c7f1fdd299ee20cc573ded517ff6989f4f9dcaf607519cf1ea35a7fc8484d64ed69e9ea31b5a59302d59cb203ae9e8#npm:18.2.0"],\ ["react-hotjar", "npm:6.1.0"],\ ["satori", "npm:0.10.1"],\ @@ -9016,6 +9020,16 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD"\ }]\ ]],\ + ["@types/react-copy-to-clipboard", [\ + ["npm:5.0.4", {\ + "packageLocation": "./.yarn/cache/@types-react-copy-to-clipboard-npm-5.0.4-cc4f7d72b2-c0fe51b79a.zip/node_modules/@types/react-copy-to-clipboard/",\ + "packageDependencies": [\ + ["@types/react-copy-to-clipboard", "npm:5.0.4"],\ + ["@types/react", "npm:18.2.15"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["@types/react-dom", [\ ["npm:18.0.11", {\ "packageLocation": "./.yarn/cache/@types-react-dom-npm-18.0.11-27f8db2995-579691e4d5.zip/node_modules/@types/react-dom/",\ @@ -11962,6 +11976,16 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD"\ }]\ ]],\ + ["copy-to-clipboard", [\ + ["npm:3.3.3", {\ + "packageLocation": "./.yarn/cache/copy-to-clipboard-npm-3.3.3-6964e6cfad-e0a325e39b.zip/node_modules/copy-to-clipboard/",\ + "packageDependencies": [\ + ["copy-to-clipboard", "npm:3.3.3"],\ + ["toggle-selection", "npm:1.0.6"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["core-js-compat", [\ ["npm:3.31.1", {\ "packageLocation": "./.yarn/cache/core-js-compat-npm-3.31.1-78e60e3eeb-9a16d69926.zip/node_modules/core-js-compat/",\ @@ -20365,6 +20389,30 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD"\ }]\ ]],\ + ["react-copy-to-clipboard", [\ + ["npm:5.1.0", {\ + "packageLocation": "./.yarn/cache/react-copy-to-clipboard-npm-5.1.0-de9742f2bc-f00a4551b9.zip/node_modules/react-copy-to-clipboard/",\ + "packageDependencies": [\ + ["react-copy-to-clipboard", "npm:5.1.0"]\ + ],\ + "linkType": "SOFT"\ + }],\ + ["virtual:f05aa4e4719fd537ba4da07d5553d5af64c7f1fdd299ee20cc573ded517ff6989f4f9dcaf607519cf1ea35a7fc8484d64ed69e9ea31b5a59302d59cb203ae9e8#npm:5.1.0", {\ + "packageLocation": "./.yarn/__virtual__/react-copy-to-clipboard-virtual-e16ddeaec0/0/cache/react-copy-to-clipboard-npm-5.1.0-de9742f2bc-f00a4551b9.zip/node_modules/react-copy-to-clipboard/",\ + "packageDependencies": [\ + ["react-copy-to-clipboard", "virtual:f05aa4e4719fd537ba4da07d5553d5af64c7f1fdd299ee20cc573ded517ff6989f4f9dcaf607519cf1ea35a7fc8484d64ed69e9ea31b5a59302d59cb203ae9e8#npm:5.1.0"],\ + ["@types/react", "npm:18.0.35"],\ + ["copy-to-clipboard", "npm:3.3.3"],\ + ["prop-types", "npm:15.8.1"],\ + ["react", "npm:18.2.0"]\ + ],\ + "packagePeers": [\ + "@types/react",\ + "react"\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["react-docgen", [\ ["npm:5.4.3", {\ "packageLocation": "./.yarn/cache/react-docgen-npm-5.4.3-10385f7edf-cef935ba94.zip/node_modules/react-docgen/",\ @@ -23080,6 +23128,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD"\ }]\ ]],\ + ["toggle-selection", [\ + ["npm:1.0.6", {\ + "packageLocation": "./.yarn/cache/toggle-selection-npm-1.0.6-c506b73005-a90dc80ed1.zip/node_modules/toggle-selection/",\ + "packageDependencies": [\ + ["toggle-selection", "npm:1.0.6"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["toidentifier", [\ ["npm:1.0.1", {\ "packageLocation": "./.yarn/cache/toidentifier-npm-1.0.1-f759712599-952c29e2a8.zip/node_modules/toidentifier/",\ diff --git a/.yarn/cache/@types-react-copy-to-clipboard-npm-5.0.4-cc4f7d72b2-c0fe51b79a.zip b/.yarn/cache/@types-react-copy-to-clipboard-npm-5.0.4-cc4f7d72b2-c0fe51b79a.zip new file mode 100644 index 00000000..93cf9561 Binary files /dev/null and b/.yarn/cache/@types-react-copy-to-clipboard-npm-5.0.4-cc4f7d72b2-c0fe51b79a.zip differ diff --git a/.yarn/cache/copy-to-clipboard-npm-3.3.3-6964e6cfad-e0a325e39b.zip b/.yarn/cache/copy-to-clipboard-npm-3.3.3-6964e6cfad-e0a325e39b.zip new file mode 100644 index 00000000..3c807193 Binary files /dev/null and b/.yarn/cache/copy-to-clipboard-npm-3.3.3-6964e6cfad-e0a325e39b.zip differ diff --git a/.yarn/cache/react-copy-to-clipboard-npm-5.1.0-de9742f2bc-f00a4551b9.zip b/.yarn/cache/react-copy-to-clipboard-npm-5.1.0-de9742f2bc-f00a4551b9.zip new file mode 100644 index 00000000..b5b7b1da Binary files /dev/null and b/.yarn/cache/react-copy-to-clipboard-npm-5.1.0-de9742f2bc-f00a4551b9.zip differ diff --git a/.yarn/cache/toggle-selection-npm-1.0.6-c506b73005-a90dc80ed1.zip b/.yarn/cache/toggle-selection-npm-1.0.6-c506b73005-a90dc80ed1.zip new file mode 100644 index 00000000..298acb63 Binary files /dev/null and b/.yarn/cache/toggle-selection-npm-1.0.6-c506b73005-a90dc80ed1.zip differ diff --git a/package.json b/package.json index 83d7d5dd..72b51d32 100644 --- a/package.json +++ b/package.json @@ -87,6 +87,7 @@ "@types/mixpanel-browser": "^2.38.1", "@types/node": "18.15.11", "@types/react": "18.0.35", + "@types/react-copy-to-clipboard": "^5", "@types/react-dom": "18.0.11", "@types/testing-library__jest-dom": "^5", "@typescript-eslint/eslint-plugin": "^5.58.0", @@ -114,6 +115,7 @@ "postcss": "^8.4.21", "postcss-syntax": "^0.36.2", "prettier": "^2.8.7", + "react-copy-to-clipboard": "^5.1.0", "storybook": "^7.0.6", "styled-jsx": "^5.1.2", "stylelint": "^15.10.1", diff --git a/src/components/copyLink/CopyLink.tsx b/src/components/copyLink/CopyLink.tsx new file mode 100644 index 00000000..82837d50 --- /dev/null +++ b/src/components/copyLink/CopyLink.tsx @@ -0,0 +1,29 @@ +import { type PropsWithChildren } from 'react'; +import { CopyToClipboard } from 'react-copy-to-clipboard'; + +import { BASE_URL } from '~/constants/url'; + +interface Props { + copyText: string; + onCopy: () => void; +} + +const CopyLink = ({ copyText, children, onCopy }: PropsWithChildren) => { + return ( + + {children} + + ); +}; + +export default CopyLink; + +const copyToClipBoardWithHostUrl = (path: string) => { + if (typeof window === 'undefined') { + return `${BASE_URL}${path}`; + } else { + const { host } = window.location; + + return `${host}${path}`; + } +}; diff --git a/src/features/dna/DnaCta.tsx b/src/features/dna/DnaCta.tsx index d4ecb94b..8ac97d0b 100644 --- a/src/features/dna/DnaCta.tsx +++ b/src/features/dna/DnaCta.tsx @@ -3,10 +3,10 @@ import { useRouter } from 'next/router'; import { css, type Theme } from '@emotion/react'; import CTAButton from '~/components/button/CTAButton'; +import CopyLink from '~/components/copyLink/CopyLink'; import useToast from '~/components/toast/useToast'; import type useGetUserInfoBySurveyId from '~/hooks/api/user/useGetUserInfoBySurveyId'; import { type DnaOwnerStatus } from '~/pages/dna/type'; -import { copyToClipBoardWithHost } from '~/utils/clipboard'; import recordEvent from '~/utils/event'; interface Props { @@ -19,10 +19,8 @@ const DnaCta: FC = ({ surveyId, dnaOwnerStatus, userInfo }) => { const router = useRouter(); const { fireToast } = useToast(); - const onClickCopyCTA = () => { + const onClickLink = () => { recordEvent({ action: 'DNA 페이지 - 커리어 명함 링크 복사 클릭' }); - - copyToClipBoardWithHost(`/dna/${surveyId}`); fireToast({ content: `${userInfo?.nickname}님의 커리어 명함 링크가 복사되었어요`, higherThanCTA: true }); }; @@ -35,7 +33,9 @@ const DnaCta: FC = ({ surveyId, dnaOwnerStatus, userInfo }) => { if (dnaOwnerStatus === 'current_user') return (
- 공유하기 + + 공유하기 +
); diff --git a/src/features/feedback/NewFeedbackCopyButton.tsx b/src/features/feedback/NewFeedbackCopyButton.tsx index 19172bbf..5afb5fe0 100644 --- a/src/features/feedback/NewFeedbackCopyButton.tsx +++ b/src/features/feedback/NewFeedbackCopyButton.tsx @@ -1,10 +1,10 @@ import { css, type Theme } from '@emotion/react'; +import CopyLink from '~/components/copyLink/CopyLink'; import LinkIcon from '~/components/icons/LinkIcon'; import Toast from '~/components/toast/Toast'; import useToast from '~/components/toast/useToast'; import { HEAD_2_BOLD } from '~/styles/typo'; -import { copyToClipBoardWithHost } from '~/utils/clipboard'; interface Props { surveyId: string; @@ -13,9 +13,7 @@ interface Props { const NewFeedbackCopyButton = ({ surveyId }: Props) => { const { fireToast } = useToast(); - const onClick = () => { - copyToClipBoardWithHost(`/review/${surveyId}`); - + const onCopyLink = () => { fireToast({ content: ( <> @@ -23,17 +21,21 @@ const NewFeedbackCopyButton = ({ surveyId }: Props) => { 나의 질문 폼 링크가 복사되었어요 ), + higherThanCTA: true, }); }; return ( - + + + ); }; diff --git a/src/pages/result/SurveyIdLoaded.tsx b/src/pages/result/SurveyIdLoaded.tsx index 3f31c1ab..4939d337 100644 --- a/src/pages/result/SurveyIdLoaded.tsx +++ b/src/pages/result/SurveyIdLoaded.tsx @@ -5,6 +5,7 @@ import { m, type Variants } from 'framer-motion'; import BottomSheet from '~/components/bottomSheet/BottomSheet'; import CTAButton from '~/components/button/CTAButton'; +import CopyLink from '~/components/copyLink/CopyLink'; import Softskill from '~/components/graphic/softskills/Softskill'; import { type Softskills } from '~/components/graphic/softskills/type'; import Header from '~/components/header/Header'; @@ -34,7 +35,6 @@ import useBoolean from '~/hooks/common/useBoolean'; import useScrollLock from '~/hooks/common/useScrollLock'; import { useScrollSpy } from '~/hooks/common/useScrollSpy'; import { BODY_2_REGULAR, HEAD_1, HEAD_2_BOLD } from '~/styles/typo'; -import { copyToClipBoardWithHost } from '~/utils/clipboard'; interface Props { surveyId: string; @@ -43,12 +43,13 @@ interface Props { const PILL_COLORS: Color[] = ['bluegreen', 'pink', 'skyblue', 'yellowgreen', 'purple']; const SurveyIdLoaded = ({ surveyId }: Props) => { + const { fireToast } = useToast(); + const { isLoading: isFeedbackSummaryLoading, data: feedbackSummaryData } = useGetFeedbackSummaryBySurveyId(surveyId); const { isLoading: isReviewersSummaryLoading, data: reviewersSummaryData } = useGetReviewersSummaryBySurveyId(surveyId); const { isLoading: isAllDataLoading, data: allData } = useGetAllFeedbacksBySurveyId(surveyId); const tendencyCountData = getTendencyCount(allData); - const { fireToast } = useToast(); const [isShowing, toggle, _, setFalse] = useBoolean(false); useScrollLock({ lock: isShowing }); @@ -63,9 +64,11 @@ const SurveyIdLoaded = ({ surveyId }: Props) => { setInnerWidth(limittedInnerWidth); }, []); - const onClickCTA = () => { - copyToClipBoardWithHost(`/review/${surveyId}`); + const moveToTop = () => { + window.scrollTo({ top: 0, behavior: 'smooth' }); + }; + const onCopyLink = () => { fireToast({ content: ( <> @@ -77,10 +80,6 @@ const SurveyIdLoaded = ({ surveyId }: Props) => { }); }; - const moveToTop = () => { - window.scrollTo({ top: 0, behavior: 'smooth' }); - }; - if (feedbackSummaryData && feedbackSummaryData.all_feedback_count < 1) { return ( <> @@ -95,9 +94,9 @@ const SurveyIdLoaded = ({ surveyId }: Props) => { 더 많은 동료들에게 질문 폼 링크를 공유해 보세요! - - 공유하기 - + + 공유하기 + ); diff --git a/src/pages/survey/link.page.tsx b/src/pages/survey/link.page.tsx index 82e097d5..fd5cf975 100644 --- a/src/pages/survey/link.page.tsx +++ b/src/pages/survey/link.page.tsx @@ -1,8 +1,10 @@ +import { useEffect } from 'react'; import Image from 'next/image'; import { css, type Theme } from '@emotion/react'; import { m } from 'framer-motion'; import CTAButton from '~/components/button/CTAButton'; +import CopyLink from '~/components/copyLink/CopyLink'; import LinkIcon from '~/components/icons/LinkIcon'; import SEO from '~/components/SEO/SEO'; import StaggerWrapper from '~/components/stagger/StaggerWrapper'; @@ -11,24 +13,23 @@ import useToast from '~/components/toast/useToast'; import { fixedBottomCss } from '~/features/review/style'; import { CTAVariants, fixedContainerCss } from '~/features/survey/styles'; import useInternalRouter from '~/hooks/router/useInternalRouter'; -import { copyToClipBoardWithHost } from '~/utils/clipboard'; const SurveyLinkPage = () => { const { fireToast } = useToast(); const router = useInternalRouter(); + const id = router.query.id; - const onNext = () => { + useEffect(() => { if (!router.isReady) { - throw new Error('잠시만 기다려주세요. '); + return; } - const id = router.query.id; if (!id) { throw new Error('잘못된 경로입니다.\nsurveyId가 없습니다.'); } + }, [id, router]); - copyToClipBoardWithHost(`/review/${id}`); - + const onCopyLink = () => { fireToast({ content: ( <> @@ -58,9 +59,11 @@ const SurveyLinkPage = () => { - - 질문 폼 공유하기 - + + + 질문 폼 공유하기 + + diff --git a/src/utils/clipboard.ts b/src/utils/clipboard.ts deleted file mode 100644 index cded4a5c..00000000 --- a/src/utils/clipboard.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { BASE_URL } from '~/constants/url'; - -export const copyToClipBoard = (text: string) => { - navigator.clipboard.writeText(text); -}; - -export const copyToClipBoardWithHost = (path: string) => { - if (typeof window === 'undefined') { - copyToClipBoard(`${BASE_URL}${path}`); - } else { - const { host } = window.location; - copyToClipBoard(`${host}${path}`); - } -}; diff --git a/yarn.lock b/yarn.lock index b9d39c0f..e0bdc718 100644 --- a/yarn.lock +++ b/yarn.lock @@ -42,6 +42,7 @@ __metadata: "@types/mixpanel-browser": ^2.38.1 "@types/node": 18.15.11 "@types/react": 18.0.35 + "@types/react-copy-to-clipboard": ^5 "@types/react-dom": 18.0.11 "@types/testing-library__jest-dom": ^5 "@typescript-eslint/eslint-plugin": ^5.58.0 @@ -78,6 +79,7 @@ __metadata: postcss-syntax: ^0.36.2 prettier: ^2.8.7 react: 18.2.0 + react-copy-to-clipboard: ^5.1.0 react-dom: 18.2.0 react-hotjar: ^6.1.0 satori: ^0.10.1 @@ -5548,6 +5550,15 @@ __metadata: languageName: node linkType: hard +"@types/react-copy-to-clipboard@npm:^5": + version: 5.0.4 + resolution: "@types/react-copy-to-clipboard@npm:5.0.4" + dependencies: + "@types/react": "*" + checksum: c0fe51b79a393d4c1fa5f8a288793c03f791e0d979abaa6ae587de53b8e3bc1f05e3379e7539077a21c9d66c12c3fad3ab6684f836f2ba7a55b9589de8500738 + languageName: node + linkType: hard + "@types/react-dom@npm:18.0.11": version: 18.0.11 resolution: "@types/react-dom@npm:18.0.11" @@ -7980,6 +7991,15 @@ __metadata: languageName: node linkType: hard +"copy-to-clipboard@npm:^3.3.1": + version: 3.3.3 + resolution: "copy-to-clipboard@npm:3.3.3" + dependencies: + toggle-selection: ^1.0.6 + checksum: e0a325e39b7615108e6c1c8ac110ae7b829cdc4ee3278b1df6a0e4228c490442cc86444cd643e2da344fbc424b3aab8909e2fec82f8bc75e7e5b190b7c24eecf + languageName: node + linkType: hard + "core-js-compat@npm:^3.25.1, core-js-compat@npm:^3.31.0": version: 3.31.1 resolution: "core-js-compat@npm:3.31.1" @@ -15093,6 +15113,18 @@ __metadata: languageName: node linkType: hard +"react-copy-to-clipboard@npm:^5.1.0": + version: 5.1.0 + resolution: "react-copy-to-clipboard@npm:5.1.0" + dependencies: + copy-to-clipboard: ^3.3.1 + prop-types: ^15.8.1 + peerDependencies: + react: ^15.3.0 || 16 || 17 || 18 + checksum: f00a4551b9b63c944a041a6ab46af5ef20ba1106b3bc25173e7ef9bffbfba17a613368682ab8820cfe8d4b3acc5335cd9ce20229145bcc1e6aa8d1db04c512e5 + languageName: node + linkType: hard + "react-docgen-typescript@npm:^2.2.2": version: 2.2.2 resolution: "react-docgen-typescript@npm:2.2.2" @@ -17407,6 +17439,13 @@ __metadata: languageName: node linkType: hard +"toggle-selection@npm:^1.0.6": + version: 1.0.6 + resolution: "toggle-selection@npm:1.0.6" + checksum: a90dc80ed1e7b18db8f4e16e86a5574f87632dc729cfc07d9ea3ced50021ad42bb4e08f22c0913e0b98e3837b0b717e0a51613c65f30418e21eb99da6556a74c + languageName: node + linkType: hard + "toidentifier@npm:1.0.1": version: 1.0.1 resolution: "toidentifier@npm:1.0.1"