Skip to content

Commit 3998345

Browse files
committed
feat: integrate codenary
1 parent 1996d30 commit 3998345

File tree

7 files changed

+187
-17
lines changed

7 files changed

+187
-17
lines changed

src/App.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { Helmet } from 'react-helmet-async';
1414
import HomePage from './pages/home/HomePage';
1515
import MainPageTemplate from './components/main/MainPageTemplate';
1616
import ConditionalBackground from './components/base/ConditionalBackground';
17+
import UserIntegratePage from './pages/UserIntegratePage';
1718

1819
const loadableConfig = {
1920
fallback: <PageTemplate />,
@@ -86,6 +87,7 @@ const App: React.FC<AppProps> = (props) => {
8687
<Route path="/lists/:type(liked|read)" component={ReadingListPage} />
8788
<Route path="/lists" render={() => <Redirect to="/lists/liked" />} />
8889
<Route path="/post-stats/:postId" component={PostStatsPage} />
90+
<Route path="/user-integrate" component={UserIntegratePage} />
8991
<Route component={NotFoundPage} />
9092
</Switch>
9193
</ErrorBoundary>

src/components/auth/AuthForm.tsx

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ export interface AuthFormProps {
7070
onSendAuthEmail: (email: string) => void;
7171
registered: boolean | null;
7272
currentPath: string;
73+
isIntegrate?: boolean;
7374
}
7475

7576
const AuthForm: React.FC<AuthFormProps> = ({
@@ -79,6 +80,7 @@ const AuthForm: React.FC<AuthFormProps> = ({
7980
loading,
8081
registered,
8182
currentPath,
83+
isIntegrate,
8284
}) => {
8385
const [email, onChangeEmail] = useInput('');
8486
const onSubmit = (email: string) => {
@@ -107,24 +109,29 @@ const AuthForm: React.FC<AuthFormProps> = ({
107109
</section>
108110
<section>
109111
<h4>소셜 계정으로 {modeText}</h4>
110-
<AuthSocialButtonGroup currentPath={currentPath} />
112+
<AuthSocialButtonGroup
113+
currentPath={currentPath}
114+
isIntegrate={isIntegrate}
115+
/>
111116
</section>
112117
</div>
113-
<div className="foot">
114-
<span>
115-
{mode === 'LOGIN'
116-
? '아직 회원이 아니신가요?'
117-
: '계정이 이미 있으신가요?'}
118-
</span>
119-
<div
120-
className="link"
121-
tabIndex={7}
122-
onClick={onToggleMode}
123-
data-testid="switchmode"
124-
>
125-
{mode === 'LOGIN' ? '회원가입' : '로그인'}
118+
{isIntegrate ? null : (
119+
<div className="foot">
120+
<span>
121+
{mode === 'LOGIN'
122+
? '아직 회원이 아니신가요?'
123+
: '계정이 이미 있으신가요?'}
124+
</span>
125+
<div
126+
className="link"
127+
tabIndex={7}
128+
onClick={onToggleMode}
129+
data-testid="switchmode"
130+
>
131+
{mode === 'LOGIN' ? '회원가입' : '로그인'}
132+
</div>
126133
</div>
127-
</div>
134+
)}
128135
</AuthFormBlock>
129136
);
130137
};

src/components/auth/AuthSocialButton.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ interface AuthSocialButtonProps {
2727
provider: 'facebook' | 'google' | 'github';
2828
tabIndex?: number;
2929
currentPath: string;
30+
isIntegrate?: boolean;
3031
}
3132

3233
const providerMap = {
@@ -51,6 +52,7 @@ const AuthSocialButton: React.FC<AuthSocialButtonProps> = ({
5152
provider,
5253
tabIndex,
5354
currentPath,
55+
isIntegrate,
5456
}) => {
5557
const info = providerMap[provider];
5658
const { icon: Icon, color, border } = info;
@@ -60,7 +62,9 @@ const AuthSocialButton: React.FC<AuthSocialButtonProps> = ({
6062
? process.env.REACT_APP_API_HOST
6163
: 'http://localhost:5000/';
6264

63-
const redirectTo = `${host}api/v2/auth/social/redirect/${provider}?next=${currentPath}`;
65+
const redirectTo = `${host}api/v2/auth/social/redirect/${provider}?next=${currentPath}&isIntegrate=${
66+
isIntegrate ? 1 : 0
67+
}`;
6468

6569
return (
6670
<AuthSocialButtonBlock

src/components/auth/AuthSocialButtonGroup.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,32 @@ const AuthSocialButtonGroupBlock = styled.div`
88
margin-top: 1.5rem;
99
`;
1010

11-
const AuthSocialButtonGroup = ({ currentPath }: { currentPath: string }) => {
11+
const AuthSocialButtonGroup = ({
12+
currentPath,
13+
isIntegrate,
14+
}: {
15+
currentPath: string;
16+
isIntegrate?: boolean;
17+
}) => {
1218
return (
1319
<AuthSocialButtonGroupBlock>
1420
<AuthSocialButton
1521
provider="github"
1622
tabIndex={4}
1723
currentPath={currentPath}
24+
isIntegrate={isIntegrate}
1825
/>
1926
<AuthSocialButton
2027
provider="google"
2128
tabIndex={5}
2229
currentPath={currentPath}
30+
isIntegrate={isIntegrate}
2331
/>
2432
<AuthSocialButton
2533
provider="facebook"
2634
tabIndex={6}
2735
currentPath={currentPath}
36+
isIntegrate={isIntegrate}
2837
/>
2938
</AuthSocialButtonGroupBlock>
3039
);
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
import React from 'react';
2+
import styled from 'styled-components';
3+
import useUser from '../../lib/hooks/useUser';
4+
import media from '../../lib/styles/media';
5+
import { themedPalette } from '../../lib/styles/themes';
6+
import { Logo } from '../../static/svg';
7+
import AuthForm from '../auth/AuthForm';
8+
import Button from '../common/Button';
9+
import { useMutation } from '@apollo/react-hooks';
10+
import {
11+
ACCEPT_INTEGRATION,
12+
AcceptIntegrationResponse,
13+
} from '../../lib/graphql/user';
14+
15+
function UserIntegrateTemplate() {
16+
const user = useUser();
17+
const [acceptIntegration] =
18+
useMutation<AcceptIntegrationResponse>(ACCEPT_INTEGRATION);
19+
20+
return (
21+
<Block>
22+
<Content>
23+
<LogoWrapper>
24+
<Logo />
25+
</LogoWrapper>
26+
{user ? (
27+
<IntegrateContent>
28+
<h3>회원 연동</h3>
29+
<p>Codenary에 다음 정보를 제공하시겠습니까?</p>
30+
<div>
31+
<DataList>
32+
<DataRow>
33+
<h4>프로필</h4>
34+
<div className="info">계정명, 프로필 사진, 이름</div>
35+
</DataRow>
36+
<DataRow>
37+
<h4>포스트</h4>
38+
<div className="info">전체 공개로 출간한 포스트</div>
39+
</DataRow>
40+
</DataList>
41+
</div>
42+
<div className="footer">
43+
<Button
44+
size="large"
45+
onClick={async () => {
46+
const result = await acceptIntegration();
47+
if (!result.data) return;
48+
window.location.href = `https://api-dev.codenary.co.kr/contents/velog/callback?code=${result.data.acceptIntegration}`;
49+
}}
50+
>
51+
승인
52+
</Button>
53+
</div>
54+
</IntegrateContent>
55+
) : (
56+
<AuthForm
57+
mode="LOGIN"
58+
onToggleMode={() => {}}
59+
onSendAuthEmail={() => {}}
60+
loading={false}
61+
registered={null}
62+
currentPath="/user-integrate"
63+
isIntegrate
64+
/>
65+
)}
66+
</Content>
67+
</Block>
68+
);
69+
}
70+
71+
const LogoWrapper = styled.div`
72+
width: 100%;
73+
padding-top: 24px;
74+
display: flex;
75+
align-items: center;
76+
justify-content: center;
77+
`;
78+
79+
const Block = styled.div`
80+
width: 100%;
81+
height: 100%;
82+
display: flex;
83+
84+
justify-content: center;
85+
`;
86+
87+
const Content = styled.div`
88+
width: 400px;
89+
${media.custom(440)} {
90+
width: 100%;
91+
padding-left: 1rem;
92+
padding-right: 1rem;
93+
}
94+
`;
95+
96+
const IntegrateContent = styled.div`
97+
padding-top: 32px;
98+
h3 {
99+
font-size: 1.3125rem;
100+
color: ${themedPalette.text1};
101+
}
102+
.footer {
103+
display: flex;
104+
justify-content: flex-end;
105+
margin-top: 24px;
106+
}
107+
`;
108+
109+
const DataList = styled.div`
110+
display: flex;
111+
gap: 0.5rem;
112+
flex-direction: column;
113+
`;
114+
const DataRow = styled.div`
115+
border-radius: 0.25rem;
116+
background: ${themedPalette.bg_element2};
117+
padding: 1rem;
118+
h4 {
119+
color: ${themedPalette.text1};
120+
margin: 0;
121+
font-size: 1rem;
122+
}
123+
.info {
124+
color: ${themedPalette.text2};
125+
margin-top: 0.5rem;
126+
font-size: 0.875rem;
127+
}
128+
`;
129+
130+
export default UserIntegrateTemplate;

src/lib/graphql/user.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,3 +163,13 @@ export const LOGOUT = gql`
163163
logout
164164
}
165165
`;
166+
167+
export const ACCEPT_INTEGRATION = gql`
168+
mutation AcceptIntegration {
169+
acceptIntegration
170+
}
171+
`;
172+
173+
export type AcceptIntegrationResponse = {
174+
acceptIntegration: string;
175+
};

src/pages/UserIntegratePage.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import React from 'react';
2+
import UserIntegrateTemplate from '../components/user-integrate/UserIntegrateTemplate';
3+
4+
function UserIntegratePage() {
5+
return <UserIntegrateTemplate />;
6+
}
7+
8+
export default UserIntegratePage;

0 commit comments

Comments
 (0)