Skip to content

Commit

Permalink
Merge branch 'fe-dev' into feature/#562
Browse files Browse the repository at this point in the history
  • Loading branch information
pakxe authored Sep 21, 2024
2 parents 33592a3 + 9bcbace commit f01464b
Show file tree
Hide file tree
Showing 13 changed files with 203 additions and 39 deletions.
1 change: 1 addition & 0 deletions client/cypress/e2e/createEvent.cy.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import CONSTANTS from '../constants/constants';
beforeEach(() => {
cy.blockSentry();
cy.blockKakao();
});

describe('Flow: λžœλ”© νŽ˜μ΄μ§€μ—μ„œλΆ€ν„° 이벀트λ₯Ό 생성 μ™„λ£Œν•˜λŠ” flow', () => {
Expand Down
8 changes: 8 additions & 0 deletions client/cypress/support/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@ Cypress.Commands.add('blockSentry', () => {
cy.intercept('POST', /.*sentry.io\/api.*/, {statusCode: 200}).as('sentry');
});

Cypress.Commands.add('blockKakao', () => {
cy.intercept('GET', 'https://t1.kakaocdn.net/kakao_js_sdk/2.7.2/kakao.min.js', {
statusCode: 200,
body: '',
}).as('blockKakao');
});

Cypress.Commands.add('interceptAPI', ({type, delay = 0, statusCode = 200}: InterceptAPIProps) => {
if (type === 'postEvent')
cy.intercept(POST_EVENT, {
Expand Down Expand Up @@ -49,6 +56,7 @@ declare global {
namespace Cypress {
interface Chainable {
blockSentry(): Chainable<void>;
blockKakao(): Chainable<void>;
interceptAPI(props: InterceptAPIProps): Chainable<void>;
createEventName(eventName: string): Chainable<void>;
}
Expand Down
5 changes: 5 additions & 0 deletions client/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@
window.amplitude.init('<%= process.env.AMPLITUDE_KEY %>');
});
</script>
<script
src="https://t1.kakaocdn.net/kakao_js_sdk/2.7.2/kakao.min.js"
integrity="sha384-TiCUE00h649CAMonG018J2ujOgDKW/kVWlChEuu4jK2vxfAAD0eZxzCKakxg55G4"
crossorigin="anonymous"
></script>
<title>ν–‰λ™λŒ€μž₯</title>
</head>
<body>
Expand Down
5 changes: 4 additions & 1 deletion client/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {ReactQueryDevtools} from '@tanstack/react-query-devtools';
import {ToastProvider} from '@hooks/useToast/ToastProvider';
import QueryClientBoundary from '@components/QueryClientBoundary/QueryClientBoundary';
import ErrorCatcher from '@components/AppErrorBoundary/ErrorCatcher';
import KakaoInitializer from '@components/KakaoInitializer/KakaoInitializer';

import {HDesignProvider} from '@HDesign/index';

Expand All @@ -23,7 +24,9 @@ const App: React.FC = () => {
<QueryClientBoundary>
<ReactQueryDevtools initialIsOpen={false} />
<NetworkStateCatcher />
<Outlet />
<KakaoInitializer>
<Outlet />
</KakaoInitializer>
</QueryClientBoundary>
</ErrorCatcher>
</ToastProvider>
Expand Down
15 changes: 15 additions & 0 deletions client/src/components/KakaoInitializer/KakaoInitializer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import {useEffect} from 'react';

const KakaoInitializer = ({children}: React.PropsWithChildren) => {
useEffect(() => {
if (!window.Kakao) return;

if (!window.Kakao.isInitialized()) {
window.Kakao.init(process.env.KAKAO_JAVASCRIPT_KEY);
}
}, []);

return children;
};

export default KakaoInitializer;
41 changes: 41 additions & 0 deletions client/src/components/ShareEventButton/ShareEventButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import CopyToClipboard from 'react-copy-to-clipboard';

import {useToast} from '@hooks/useToast/useToast';

import useShareEvent from '@hooks/useShareEvent';

import {Button} from '@components/Design';

import isMobileDevice from '@utils/isMobileDevice';

const ShareEventButton = () => {
const {showToast} = useToast();

const isMobile = isMobileDevice();
const {shareText, onShareButtonClick} = useShareEvent(isMobile);

return isMobile ? (
<Button size="small" variants="secondary" onClick={onShareButtonClick}>
μΉ΄μΉ΄μ˜€ν†‘μœΌλ‘œ μ •μ‚° μ΄ˆλŒ€ν•˜κΈ°
</Button>
) : (
<CopyToClipboard
text={shareText}
onCopy={() =>
showToast({
showingTime: 3000,
message: '링크가 λ³΅μ‚¬λ˜μ—ˆμ–΄μš” :) \nμ°Έμ—¬μžλ“€μ—κ²Œ 링크λ₯Ό κ³΅μœ ν•΄ μ£Όμ„Έμš”!',
type: 'confirm',
position: 'bottom',
bottom: '8rem',
})
}
>
<Button size="small" variants="secondary" onClick={onShareButtonClick}>
μ •μ‚° μ΄ˆλŒ€ν•˜κΈ°
</Button>
</CopyToClipboard>
);
};

export default ShareEventButton;
1 change: 1 addition & 0 deletions client/src/components/ShareEventButton/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export {default as ShareEventButton} from './ShareEventButton';
1 change: 1 addition & 0 deletions client/src/global.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ declare namespace NodeJS {
// env keys
readonly API_BASE_URL: string;
readonly AMPLITUDE_KEY: string;
readonly KAKAO_JAVASCRIPT_KEY: string;
}
}
23 changes: 23 additions & 0 deletions client/src/hooks/useEventPageLayout.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import {useMatch} from 'react-router-dom';

import {ROUTER_URLS} from '@constants/routerUrls';

import useNavSwitch from './useNavSwitch';
import useRequestGetEvent from './queries/event/useRequestGetEvent';

const useEventPageLayout = () => {
const navProps = useNavSwitch();
const {eventName} = useRequestGetEvent();

const isAdmin = useMatch(ROUTER_URLS.eventManage) !== null;
const isLoginPage = useMatch(ROUTER_URLS.eventLogin) !== null;

return {
navProps,
isAdmin,
eventName,
isLoginPage,
};
};

export default useEventPageLayout;
51 changes: 51 additions & 0 deletions client/src/hooks/useShareEvent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import getEventIdByUrl from '@utils/getEventIdByUrl';
import getEventPageUrlByEnvironment from '@utils/getEventPageUrlByEnvironment';

import useRequestGetEvent from './queries/event/useRequestGetEvent';

const useShareEvent = (isMobile: boolean) => {
const {eventName} = useRequestGetEvent();

const eventId = getEventIdByUrl();
const url = getEventPageUrlByEnvironment(eventId, 'home');

const shareInfo = {
title: `[ν–‰λ™λŒ€μž₯]\n${eventName}에 λŒ€ν•œ 정산을 μ‹œμž‘ν• κ²Œμš”:)`,
text: 'μ•„λž˜ 링크에 μ ‘μ†ν•΄μ„œ μ •μ‚° 내역을 확인해 μ£Όμ„Έμš”!',
url,
};

// λͺ¨λ°”일이 μ•„λ‹Œ κΈ°κΈ°λŠ” λ‹¨μˆœ ν…μŠ€νŠΈ 볡사
// λͺ¨λ°”일 κΈ°κΈ°μ—μ„œλŠ” μΉ΄μΉ΄μ˜€ν†‘ 곡유λ₯Ό μ‚¬μš©
const onShareButtonClick = () => {
if (!isMobile) return;

kakaoShare();
};

const kakaoShare = () => {
window.Kakao.Share.sendDefault({
objectType: 'feed',
content: {
title: shareInfo.title,
description: shareInfo.text,
imageUrl:
'https://wooteco-crew-wiki.s3.ap-northeast-2.amazonaws.com/%EC%9B%A8%EB%94%94%286%EA%B8%B0%29/g583lirp8yg.jpg',
link: {
mobileWebUrl: url,
webUrl: url,
},
},
buttonTitle: 'μ •μ‚° ν™•μΈν•˜κΈ°',
});
};

const shareText = `${shareInfo.title}\n${shareInfo.text}\n${url}`;

return {
shareText,
onShareButtonClick,
};
};

export default useShareEvent;
45 changes: 7 additions & 38 deletions client/src/pages/EventPage/EventPageLayout.tsx
Original file line number Diff line number Diff line change
@@ -1,61 +1,30 @@
import {Outlet, useMatch} from 'react-router-dom';
import CopyToClipboard from 'react-copy-to-clipboard';
import {Outlet} from 'react-router-dom';

import {useToast} from '@hooks/useToast/useToast';
import useRequestGetEvent from '@hooks/queries/event/useRequestGetEvent';
import useEventPageLayout from '@hooks/useEventPageLayout';

import useNavSwitch from '@hooks/useNavSwitch';
import {ShareEventButton} from '@components/ShareEventButton';

import {MainLayout, TopNav, Switch, Button} from '@HDesign/index';

import getEventIdByUrl from '@utils/getEventIdByUrl';
import getEventPageUrlByEnvironment from '@utils/getEventPageUrlByEnvironment';

import {ROUTER_URLS} from '@constants/routerUrls';
import {MainLayout, TopNav, Switch} from '@HDesign/index';

export type EventPageContextProps = {
isAdmin: boolean;
eventName: string;
};

const EventPageLayout = () => {
const {nav, paths, onChange} = useNavSwitch();
const {eventName} = useRequestGetEvent();
const eventId = getEventIdByUrl();

const isAdmin = useMatch(ROUTER_URLS.eventManage) !== null;
const isLoginPage = useMatch(ROUTER_URLS.eventLogin) !== null;
const {navProps, isAdmin, isLoginPage, eventName} = useEventPageLayout();
const {nav, paths, onChange} = navProps;

const outletContext: EventPageContextProps = {
isAdmin,
eventName,
};

const {showToast} = useToast();
const url = getEventPageUrlByEnvironment(eventId, 'home');

return (
<MainLayout backgroundColor="gray">
<TopNav>
<Switch value={nav} values={paths} onChange={onChange} />
{!isLoginPage && (
<CopyToClipboard
text={`[ν–‰λ™λŒ€μž₯]\n"${eventName}"에 λŒ€ν•œ 정산을 μ‹œμž‘ν• κ²Œμš”:)\nμ•„λž˜ 링크에 μ ‘μ†ν•΄μ„œ μ •μ‚° 내역을 확인해 μ£Όμ„Έμš”!\n${url}`}
onCopy={() =>
showToast({
showingTime: 3000,
message: '링크가 λ³΅μ‚¬λ˜μ—ˆμ–΄μš” :) \nμ°Έμ—¬μžλ“€μ—κ²Œ 링크λ₯Ό κ³΅μœ ν•΄ μ£Όμ„Έμš”!',
type: 'confirm',
position: 'bottom',
bottom: '8rem',
})
}
>
<Button size="small" variants="secondary">
μ •μ‚° μ΄ˆλŒ€ν•˜κΈ°
</Button>
</CopyToClipboard>
)}
{!isLoginPage && <ShareEventButton />}
</TopNav>
<Outlet context={outletContext} />
</MainLayout>
Expand Down
38 changes: 38 additions & 0 deletions client/src/types/kakao.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
declare global {
interface Window {
Kakao: typeof Kakao;
}
}

namespace Kakao {
function init(appKey: string): void;
function isInitialized(): boolean;

namespace Share {
function cleanup(): void;
function sendDefault(params: sendDefaultParams): void;

interface sendDefaultParams {
objectType: 'feed';
content: {
title: string;
imageUrl: string;
imageWidth?: number;
imageHeight?: number;
description: string;
link: {
mobileWebUrl: string;
webUrl: string;
};
};
buttonTitle?: string;
buttons?: {
title: string;
link: {
webUrl: string;
mobileWebUrl: string;
};
}[];
}
}
}
8 changes: 8 additions & 0 deletions client/src/utils/isMobileDevice.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const isMobileDevice = () => {
const userAgent = window.navigator.userAgent;
const mobileRegex = [/Android/i, /iPhone/i, /iPad/i, /iPod/i, /BlackBerry/i, /Windows Phone/i];

return mobileRegex.some(mobile => userAgent.match(mobile));
};

export default isMobileDevice;

0 comments on commit f01464b

Please sign in to comment.