Skip to content

Commit

Permalink
Merge pull request #133 from softeerbootcamp4th/feat/#126-share-link
Browse files Browse the repository at this point in the history
[Feat] 공유 링크 구현
  • Loading branch information
jhj2713 authored Aug 12, 2024
2 parents bbb3ad0 + 5278c1e commit 61f8acb
Show file tree
Hide file tree
Showing 9 changed files with 87 additions and 18 deletions.
2 changes: 2 additions & 0 deletions admin/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,5 @@ dist-ssr
*.njsproj
*.sln
*.sw?

.env
22 changes: 22 additions & 0 deletions client/src/apis/linkAPI.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { GetShareLinkResponse } from "@/types/linkApi";
import { fetchWithTimeout } from "@/utils/fetchWithTimeout";

const baseURL = `${import.meta.env.VITE_API_URL}/link`;
const headers = {
"Content-Type": "application/json",
};

export const LinkAPI = {
async getShareLink(token: string): Promise<GetShareLinkResponse> {
try {
const response = await fetchWithTimeout(`${baseURL}`, {
method: "POST",
headers: { ...headers, Authorization: `Bearer ${token}` },
});
return response.json();
} catch (error) {
console.error("Error:", error);
throw error;
}
},
};
1 change: 0 additions & 1 deletion client/src/constants/Auth/token.ts

This file was deleted.

4 changes: 4 additions & 0 deletions client/src/constants/cookie.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export const COOKIE_KEY = {
ACCESS_TOKEN: "token",
INVITE_USER: "referrerId",
} as const;
29 changes: 24 additions & 5 deletions client/src/features/CasperCustom/CasperCustomFinish.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@ import { useEffect, useRef } from "react";
import { motion } from "framer-motion";
import { useCookies } from "react-cookie";
import { Link } from "react-router-dom";
import { LinkAPI } from "@/apis/linkAPI";
import { LotteryAPI } from "@/apis/lotteryAPI";
import CTAButton from "@/components/CTAButton";
import { COOKIE_TOKEN_KEY } from "@/constants/Auth/token";
import { MAX_APPLY } from "@/constants/CasperCustom/customStep";
import { DISSOLVE } from "@/constants/animation";
import { COOKIE_KEY } from "@/constants/cookie";
import useCasperCustomDispatchContext from "@/hooks/useCasperCustomDispatchContext";
import useCasperCustomStateContext from "@/hooks/useCasperCustomStateContext";
import useFetch from "@/hooks/useFetch";
import useToast from "@/hooks/useToast";
import { CASPER_ACTION } from "@/types/casperCustom";
import { GetApplyCountResponse } from "@/types/lotteryApi";
import { saveDomImage } from "@/utils/saveDomImage";
Expand All @@ -27,10 +29,11 @@ export function CasperCustomFinish({
handleResetStep,
unblockNavigation,
}: CasperCustomFinishProps) {
const [cookies] = useCookies([COOKIE_TOKEN_KEY]);
const [cookies] = useCookies([COOKIE_KEY.ACCESS_TOKEN]);
const { showToast, ToastComponent } = useToast("링크가 복사되었어요!");

const { data: applyCountData, fetchData: getApplyCount } = useFetch<GetApplyCountResponse>(() =>
LotteryAPI.getApplyCount(cookies[COOKIE_TOKEN_KEY])
LotteryAPI.getApplyCount(cookies[COOKIE_KEY.ACCESS_TOKEN])
);

const dispatch = useCasperCustomDispatchContext();
Expand All @@ -39,7 +42,7 @@ export function CasperCustomFinish({
const casperCustomRef = useRef<HTMLDivElement>(null);

useEffect(() => {
if (!cookies[COOKIE_TOKEN_KEY]) {
if (!cookies[COOKIE_KEY.ACCESS_TOKEN]) {
return;
}

Expand All @@ -60,6 +63,17 @@ export function CasperCustomFinish({
dispatch({ type: CASPER_ACTION.RESET_CUSTOM });
};

const handleClickShareButton = async () => {
const link = await LinkAPI.getShareLink(cookies[COOKIE_KEY.ACCESS_TOKEN]);

try {
await navigator.clipboard.writeText(link.shortenLocalUrl);
showToast();
} catch (err) {
console.error("Failed to copy: ", err);
}
};

return (
<motion.div className="mt-[60px] flex flex-col items-center" {...SCROLL_MOTION(DISSOLVE)}>
<div className="flex items-center gap-[107px]">
Expand Down Expand Up @@ -102,7 +116,10 @@ export function CasperCustomFinish({
</div>
)}

<CTAButton label="이벤트 공유해서 추가 응모하기" />
<CTAButton
label="이벤트 공유해서 추가 응모하기"
onClick={handleClickShareButton}
/>
</div>
</div>

Expand All @@ -112,6 +129,8 @@ export function CasperCustomFinish({
</p>
<ArrowIcon stroke="#ffffff" />
</Link>

{ToastComponent}
</motion.div>
);
}
17 changes: 12 additions & 5 deletions client/src/features/CasperCustom/CasperCustomForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import { useCookies } from "react-cookie";
import { LotteryAPI } from "@/apis/lotteryAPI";
import CTAButton from "@/components/CTAButton";
import TextField from "@/components/TextField";
import { COOKIE_TOKEN_KEY } from "@/constants/Auth/token";
import { CUSTOM_OPTION } from "@/constants/CasperCustom/casper";
import { DISSOLVE } from "@/constants/animation";
import { COOKIE_KEY } from "@/constants/cookie";
import useCasperCustomDispatchContext from "@/hooks/useCasperCustomDispatchContext";
import useCasperCustomStateContext from "@/hooks/useCasperCustomStateContext";
import useFetch from "@/hooks/useFetch";
Expand All @@ -20,14 +20,17 @@ interface CasperCustomFormProps {
}

export function CasperCustomForm({ navigateNextStep }: CasperCustomFormProps) {
const [cookies] = useCookies([COOKIE_TOKEN_KEY]);
const [cookies] = useCookies([COOKIE_KEY.ACCESS_TOKEN, COOKIE_KEY.INVITE_USER]);

const {
data: casper,
isSuccess: isSuccessPostCasper,
fetchData: postCasper,
} = useFetch<PostCasperResponse, { token: string; casper: CasperInformationType }>(
({ token, casper }) => LotteryAPI.postCasper(token, casper)
} = useFetch<
PostCasperResponse,
{ token: string; referrerId: string; casper: CasperInformationType }
>(({ token, referrerId, casper }) =>
LotteryAPI.postCasper(token, { ...casper, [COOKIE_KEY.INVITE_USER]: referrerId })
);

const { casperName, expectations, selectedCasperIdx } = useCasperCustomStateContext();
Expand Down Expand Up @@ -80,7 +83,11 @@ export function CasperCustomForm({ navigateNextStep }: CasperCustomFormProps) {
expectation: expectations,
};

await postCasper({ token: cookies[COOKIE_TOKEN_KEY], casper });
await postCasper({
token: cookies[COOKIE_KEY.ACCESS_TOKEN],
referrerId: cookies[COOKIE_KEY.INVITE_USER],
casper,
});
};

return (
Expand Down
20 changes: 14 additions & 6 deletions client/src/pages/Lottery/index.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { useCallback, useEffect, useState } from "react";
import { useCookies } from "react-cookie";
import { useLoaderData, useNavigate } from "react-router-dom";
import { useLoaderData, useLocation, useNavigate } from "react-router-dom";
import { AuthAPI } from "@/apis/authAPI";
import Footer from "@/components/Footer";
import Notice from "@/components/Notice";
import { COOKIE_TOKEN_KEY } from "@/constants/Auth/token";
import { LOTTERY_SECTIONS } from "@/constants/PageSections/sections.ts";
import { COOKIE_KEY } from "@/constants/cookie";
import {
CustomDesign,
HeadLamp,
Expand Down Expand Up @@ -33,11 +33,16 @@ export default function Lottery() {
useScrollTop();

const navigate = useNavigate();
const location = useLocation();
const queryParams = new URLSearchParams(location.search);
const inviteUser = queryParams.get(COOKIE_KEY.INVITE_USER);

const lotteryData = useLoaderData() as GetLotteryResponse;

const containerRef = useHeaderStyleObserver({
darkSections: [LOTTERY_SECTIONS.HEADLINE, LOTTERY_SECTIONS.SHORT_CUT],
});
const [_cookies, setCookie] = useCookies([COOKIE_TOKEN_KEY]);
const [_cookies, setCookie] = useCookies([COOKIE_KEY.ACCESS_TOKEN, COOKIE_KEY.INVITE_USER]);

const {
data: authToken,
Expand All @@ -52,11 +57,14 @@ export default function Lottery() {

const [phoneNumberState, setPhoneNumberState] = useState(phoneNumber);

const lotteryData = useLoaderData() as GetLotteryResponse;

useEffect(() => {
if (inviteUser) {
setCookie(COOKIE_KEY.INVITE_USER, inviteUser);
}
}, [inviteUser]);
useEffect(() => {
if (authToken && isSuccessGetAuthToken) {
setCookie(COOKIE_TOKEN_KEY, authToken.accessToken);
setCookie(COOKIE_KEY.ACCESS_TOKEN, authToken.accessToken);
dispatch({ type: PHONE_NUMBER_ACTION.SET_PHONE_NUMBER, payload: phoneNumberState });
navigate("/lottery/custom");
}
Expand Down
4 changes: 4 additions & 0 deletions client/src/types/linkApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface GetShareLinkResponse {
shortenUrl: string;
shortenLocalUrl: string;
}
6 changes: 5 additions & 1 deletion client/src/types/lotteryApi.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { COOKIE_KEY } from "@/constants/cookie";

export interface CasperInformationType {
eyeShape: number;
eyePosition: number;
Expand All @@ -12,7 +14,9 @@ export type GetCasperListResponse = ({
casperId: number;
} & CasperInformationType)[];

export interface PostCasperRequestBody extends CasperInformationType {}
export interface PostCasperRequestBody extends CasperInformationType {
[COOKIE_KEY.INVITE_USER]: string;
}

export interface PostCasperResponse extends CasperInformationType {
casperId: number;
Expand Down

0 comments on commit 61f8acb

Please sign in to comment.