Skip to content

Commit

Permalink
Merge pull request #2 from Nexters/feature/add-download-link
Browse files Browse the repository at this point in the history
다운로드 링크 배너 추가
  • Loading branch information
ooooorobo committed Aug 12, 2023
2 parents d5a72a8 + 531e885 commit b5483e1
Show file tree
Hide file tree
Showing 15 changed files with 202 additions and 33 deletions.
57 changes: 57 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Expand Up @@ -23,6 +23,7 @@
"@types/koa": "^2.13.7",
"@types/koa__router": "^12.0.0",
"@types/koa-static": "^4.0.2",
"@types/koa-useragent": "^2.1.2",
"@types/node": "^20.4.5",
"@types/react-dom": "^18.2.7",
"@typescript-eslint/eslint-plugin": "^6.2.0",
Expand All @@ -33,6 +34,7 @@
"eslint": "^8.46.0",
"eslint-config-prettier": "^8.9.0",
"eslint-plugin-prettier": "^5.0.0",
"koa-useragent": "^4.1.0",
"mini-css-extract-plugin": "^2.7.6",
"npm-run-all": "^4.1.5",
"prettier": "^3.0.0",
Expand Down
Binary file added src/assets/image/logo.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
29 changes: 17 additions & 12 deletions src/client/renderer.tsx
Expand Up @@ -23,27 +23,32 @@ const defaultHtml = (assetPath: string, content: string) => `
</body>
</html>`;

type AppProps = {
assetPath: string;
isMobile: boolean;
appDownloadUrl: string;
};

export const renderer = ({
assetPath,
store,
...props
}: {
assetPath: string;
store: Store;
}) => {
} & AppProps) => {
try {
const content = renderToString(<App store={store} assetPath={assetPath} />);
return defaultHtml(assetPath, content);
const content = renderToString(<App store={store} {...props} />);
return defaultHtml(props.assetPath, content);
} catch (e) {
return renderNotFound(assetPath);
return renderNotFound(props);
}
};

export const renderNotFound = (assetPath: string) => {
const content = renderToString(<NotFoundPage assetPath={assetPath} />);
return defaultHtml(assetPath, content);
export const renderNotFound = (props: AppProps) => {
const content = renderToString(<NotFoundPage {...props} />);
return defaultHtml(props.assetPath, content);
};

export const renderExpired = (assetPath: string) => {
const content = renderToString(<ExpiredPage assetPath={assetPath} />);
return defaultHtml(assetPath, content);
export const renderExpired = (props: AppProps) => {
const content = renderToString(<ExpiredPage {...props} />);
return defaultHtml(props.assetPath, content);
};
10 changes: 5 additions & 5 deletions src/client/view/App.tsx
Expand Up @@ -4,18 +4,18 @@ import { BandalartSharePage } from './pages/share';
import { Provider } from 'react-redux';
import { Store } from 'redux';
import { DefaultContainer } from './components/_common/DefaultContainer';
import { AppProps } from './types/app';

initApiClient();

type AppProps = {
type Props = {
store: Store;
assetPath: string;
};
} & AppProps;

export const App = ({ store, assetPath }: AppProps) => {
export const App = ({ store, ...props }: Props) => {
return (
<Provider store={store}>
<DefaultContainer assetPath={assetPath}>
<DefaultContainer {...props}>
<BandalartSharePage />
</DefaultContainer>
</Provider>
Expand Down
1 change: 1 addition & 0 deletions src/client/view/components/HeaderSection.tsx
Expand Up @@ -47,6 +47,7 @@ export const HeaderSection = ({
};

const container = css`
padding-top: 20px;
width: 100%;
display: flex;
flex-direction: column;
Expand Down
6 changes: 4 additions & 2 deletions src/client/view/components/_common/DefaultContainer.tsx
Expand Up @@ -6,10 +6,12 @@ import { EnvContextProvider } from '../context/EnvContext';
type Props = {
children: ReactNode;
assetPath: string;
isMobile: boolean;
appDownloadUrl: string;
};

export const DefaultContainer = ({ assetPath, children }: Props) => (
export const DefaultContainer = ({ children, ...props }: Props) => (
<div className={cx(globalStyle, 'theme-light')}>
<EnvContextProvider assetPath={assetPath}>{children}</EnvContextProvider>
<EnvContextProvider {...props}>{children}</EnvContextProvider>
</div>
);
72 changes: 72 additions & 0 deletions src/client/view/components/banner/AppDownload.tsx
@@ -0,0 +1,72 @@
import React, { useContext } from 'react';
import { css } from '@linaria/core';
import { EnvContext } from '../context/EnvContext';

export const AppDownload = () => {
const { assetPath, appDownloadUrl } = useContext(EnvContext);
return (
<div className={container}>
<div className={logoContainer}>
<img src={`${assetPath}/image/logo.png`} alt={'app logo'} />
</div>
<span className={title}>지금 나만의 반다라트를 만들어봐요</span>
<a
className={downloadButton}
href={appDownloadUrl}
target={'_blank'}
rel={'noreferrer noopener'}
title={'앱 다운로드'}
>
앱 다운로드
</a>
</div>
);
};

const container = css`
position: fixed;
top: 0;
left: 0;
right: 0;
background-color: #fff;
height: 62px;
display: flex;
align-items: center;
padding: 0 16px;
border-bottom: 1px solid var(--color-100);
`;

const logoContainer = css`
width: 37px;
height: 37px;
border-radius: 16px;
display: flex;
filter: drop-shadow(0px 1px 1px rgba(17, 24, 39, 0.1));
margin-right: 12px;
`;

const title = css`
color: var(--color-700);
font-size: 12px;
font-weight: 500;
letter-spacing: -0.24px;
`;

const downloadButton = css`
padding: 6px 12px;
background-color: var(--color-700);
border-radius: 88px;
text-align: center;
// font
font-weight: 700;
font-size: 12px;
letter-spacing: -0.24px;
color: #fff;
text-decoration: none;
margin-left: auto;
`;
8 changes: 6 additions & 2 deletions src/client/view/components/context/EnvContext.tsx
Expand Up @@ -2,20 +2,24 @@ import React, { createContext, PropsWithChildren } from 'react';

type EnvContextState = {
assetPath: string;
isMobile: boolean;
appDownloadUrl: string;
};

const initialState: EnvContextState = {
assetPath: '',
isMobile: false,
appDownloadUrl: '',
};

export const EnvContext = createContext(initialState);

export const EnvContextProvider = ({
children,
assetPath,
...props
}: PropsWithChildren<EnvContextState>) => {
return (
<EnvContext.Provider value={{ ...initialState, assetPath }}>
<EnvContext.Provider value={{ ...initialState, ...props }}>
{children}
</EnvContext.Provider>
);
Expand Down
7 changes: 5 additions & 2 deletions src/client/view/pages/404/index.tsx
Expand Up @@ -2,10 +2,13 @@ import React from 'react';
import { WarningTemplate } from '../../components/template/WarningTemplate';
import { DefaultContainer } from '../../components/_common/DefaultContainer';
import { css } from '@linaria/core';
import { AppProps } from '../../types/app';
import { AppDownload } from '../../components/banner/AppDownload';

export const NotFoundPage = ({ assetPath }: { assetPath: string }) => (
<DefaultContainer assetPath={assetPath}>
export const NotFoundPage = (props: AppProps) => (
<DefaultContainer {...props}>
<div className={container}>
{props.isMobile && <AppDownload />}
<WarningTemplate
iconName={'warning'}
title={'존재하지 않는 페이지에요'}
Expand Down
7 changes: 5 additions & 2 deletions src/client/view/pages/expired/index.tsx
Expand Up @@ -2,10 +2,13 @@ import React from 'react';
import { WarningTemplate } from '../../components/template/WarningTemplate';
import { DefaultContainer } from '../../components/_common/DefaultContainer';
import { css } from '@linaria/core';
import { AppProps } from '../../types/app';
import { AppDownload } from '../../components/banner/AppDownload';

export const ExpiredPage = ({ assetPath }: { assetPath: string }) => (
<DefaultContainer assetPath={assetPath}>
export const ExpiredPage = (props: AppProps) => (
<DefaultContainer {...props}>
<div className={container}>
{props.isMobile && <AppDownload />}
<WarningTemplate
iconName={'hourglass'}
title={'유효기간이 만료된 페이지에요'}
Expand Down
7 changes: 6 additions & 1 deletion src/client/view/pages/share/index.tsx
@@ -1,17 +1,21 @@
import React, { CSSProperties } from 'react';
import React, { CSSProperties, useContext } from 'react';
import { css } from '@linaria/core';
import { useSelector } from 'react-redux';
import { RootState } from '../../../stores/createStore';
import { HeaderSection } from '../../components/HeaderSection';
import { Table } from '../../components/table/Table';
import { TABLE_SIZE } from '../../constants/table';
import { AppDownload } from '../../components/banner/AppDownload';
import { EnvContext } from '../../components/context/EnvContext';

export const BandalartSharePage = () => {
const detail = useSelector((state: RootState) => state.bandalartDetail);
const treeRoot = useSelector((state: RootState) => state.bandalartTree)!;

const completionRatio = treeRoot.completionRatio;

const { isMobile } = useContext(EnvContext);

return (
<div
className={container}
Expand All @@ -22,6 +26,7 @@ export const BandalartSharePage = () => {
} as CSSProperties
}
>
{isMobile && <AppDownload />}
<HeaderSection detail={detail} completionRatio={completionRatio} />
<main>
<Table root={treeRoot} />
Expand Down
5 changes: 5 additions & 0 deletions src/client/view/types/app.ts
@@ -0,0 +1,5 @@
export type AppProps = {
assetPath: string;
isMobile: boolean;
appDownloadUrl: string;
};

0 comments on commit b5483e1

Please sign in to comment.