Skip to content

Commit 8472df7

Browse files
committed
Implements formatDate
1 parent 55e851f commit 8472df7

File tree

11 files changed

+95
-14
lines changed

11 files changed

+95
-14
lines changed

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
"@loadable/webpack-plugin": "^5.5.0",
1111
"@svgr/webpack": "4.1.0",
1212
"@types/codemirror": "^0.0.72",
13+
"@types/date-fns": "^2.6.0",
1314
"@types/graphql": "^14.0.7",
1415
"@types/highlight.js": "^9.12.3",
1516
"@types/isomorphic-fetch": "^0.0.35",
@@ -51,6 +52,7 @@
5152
"case-sensitive-paths-webpack-plugin": "2.2.0",
5253
"codemirror": "^5.45.0",
5354
"css-loader": "1.0.0",
55+
"date-fns": "^1.30.1",
5456
"dotenv": "6.0.0",
5557
"dotenv-expand": "4.2.0",
5658
"eslint-config-prettier": "^4.2.0",

src/components/base/Header.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import * as React from 'react';
22
import styled, { css } from 'styled-components';
3-
import { Logo } from '../../static/svg';
43
import { breakpoints } from '../../lib/styles/responsive';
54
import RoundButton from '../common/RoundButton';
65
import { CurrentUser } from '../../lib/graphql/user';
@@ -10,7 +9,6 @@ import HeaderUserMenu from './HeaderUserMenu';
109
import { logout } from '../../lib/api/auth';
1110
import storage from '../../lib/storage';
1211
import { UserLogo } from '../../modules/header';
13-
import { Link } from 'react-router-dom';
1412
import HeaderLogo from './HeaderLogo';
1513

1614
const HeaderBlock = styled.div<{

src/components/post/PostHead.tsx

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import * as React from 'react';
22
import styled from 'styled-components';
33
import VelogResponsive from '../velog/VelogResponsive';
44
import palette from '../../lib/styles/palette';
5+
import { formatDate } from '../../lib/utils';
56

67
const PostHeadBlock = styled(VelogResponsive)`
78
margin-top: 5.5rem;
@@ -12,20 +13,39 @@ const PostHeadBlock = styled(VelogResponsive)`
1213
margin-top: 0;
1314
font-weight: 800;
1415
color: ${palette.gray8};
16+
margin-bottom: 2rem;
17+
}
18+
`;
19+
20+
const SubInfo = styled.div`
21+
font-size: 1rem;
22+
color: ${palette.gray7};
23+
.username {
24+
color: ${palette.gray8};
25+
font-weight: bold;
26+
}
27+
.separator {
28+
margin-left: 0.5rem;
29+
margin-right: 0.5rem;
1530
}
1631
`;
1732

1833
export interface PostHeadProps {
1934
title: string;
2035
tags: string[];
2136
username: string;
22-
date: string | number;
37+
date: string;
2338
}
2439

25-
const PostHead: React.FC<PostHeadProps> = ({ title }) => {
40+
const PostHead: React.FC<PostHeadProps> = ({ title, username, date }) => {
2641
return (
2742
<PostHeadBlock>
2843
<h1>{title}</h1>
44+
<SubInfo>
45+
<span className="username">{username}</span>
46+
<span className="separator">&middot;</span>
47+
<span>{formatDate(date)}</span>
48+
</SubInfo>
2949
</PostHeadBlock>
3050
);
3151
};

src/components/post/__tests__/PostHead.test.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ describe('PostHead', () => {
88
title: 'title',
99
tags: ['tagA', 'tagB'],
1010
username: 'velopert',
11-
date: new Date(Date.now() - 1000 * 60 * 60 * 5),
11+
date: new Date(Date.now() - 1000 * 60 * 60 * 5).toString(),
1212
};
1313
const utils = render(<PostHead {...initialProps} {...props} />);
1414
return {
@@ -19,7 +19,11 @@ describe('PostHead', () => {
1919
const { getByText } = setup();
2020
getByText('title');
2121
});
22-
it('renders tags', () => {});
23-
it('renders username', () => {});
22+
it('renders username', () => {
23+
const { getByText } = setup();
24+
getByText('velopert');
25+
});
2426
it('renders date', () => {});
27+
28+
it('renders tags', () => {});
2529
});

src/containers/post/PostViewer.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import * as React from 'react';
2-
import styled from 'styled-components';
32
import { Query, QueryResult } from 'react-apollo';
43
import { READ_POST, SinglePost } from '../../lib/graphql/post';
54
import PostHead from '../../components/post/PostHead';
@@ -19,7 +18,7 @@ const PostViewer: React.FC<PostViewerProps> = ({ username, urlSlug }) => {
1918
}}
2019
>
2120
{({ loading, error, data }: QueryResult<{ post: SinglePost }>) => {
22-
console.log(data);
21+
console.log(error);
2322
if (loading) return null; // TODO: show placeholder
2423
if (!data || !data.post) return null;
2524
if (error) return null; // SHOW ERROR

src/lib/__tests__/utils.test.ts

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { escapeForUrl } from '../utils';
1+
import { escapeForUrl, formatDate } from '../utils';
22

33
describe('utils', () => {
44
it('escapeForUrl', () => {
@@ -16,4 +16,30 @@ describe('utils', () => {
1616
'trim',
1717
]);
1818
});
19+
20+
describe('formatDate util function', () => {
21+
const now = Date.now();
22+
const justNow = new Date(now - 1000 * 60).toString();
23+
const fiveMinsBefore = new Date(now - 1000 * 60 * 5).toString();
24+
const yesterday = new Date(now - 1000 * 60 * 60 * 24).toString();
25+
const twoDaysAgo = new Date(now - 1000 * 60 * 60 * 24 * 2).toString();
26+
const tenDaysAgo = new Date(now - 1000 * 60 * 60 * 24 * 10).toString();
27+
it('shows just now', () => {
28+
expect(formatDate(justNow)).toBe('방금 전');
29+
});
30+
it('shows five minutes before', () => {
31+
expect(formatDate(fiveMinsBefore)).toBe('5분 전');
32+
});
33+
it('shows yesterday', () => {
34+
expect(formatDate(yesterday)).toBe('어제');
35+
});
36+
it('shows two days ago', () => {
37+
expect(formatDate(twoDaysAgo)).toBe('2일 전');
38+
});
39+
it('shows ten days ago as a date', () => {
40+
const result = formatDate(tenDaysAgo);
41+
const match = /^\d{4} \d{1,2} \d{1,2}$/.test(result);
42+
expect(match).toBeTruthy();
43+
});
44+
});
1945
});

src/lib/graphql/post.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ export const READ_POST = gql`
9898
is_private
9999
is_temp
100100
user {
101+
id
101102
username
102103
profile {
103104
display_name

src/lib/utils.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,26 @@
1+
import distanceInWordsToNow from 'date-fns/distance_in_words_to_now';
2+
import format from 'date-fns/format';
3+
import koLocale from 'date-fns/locale/ko';
4+
5+
export const formatDate = (date: string): string => {
6+
const now = Date.now();
7+
const diff = now - new Date(date).getTime();
8+
// less than 5 minutes
9+
if (diff < 1000 * 60 * 5) {
10+
return '방금 전';
11+
}
12+
if (diff < 1000 * 60 * 60 * 24) {
13+
return distanceInWordsToNow(date, { addSuffix: true, locale: koLocale });
14+
}
15+
if (diff < 1000 * 60 * 60 * 36) {
16+
return '어제';
17+
}
18+
if (diff < 1000 * 60 * 60 * 24 * 7) {
19+
return distanceInWordsToNow(date, { addSuffix: true, locale: koLocale });
20+
}
21+
return format(date, 'YYYY년 M월 D일');
22+
};
23+
124
export const getScrollTop = () => {
225
if (!document.body) return 0;
326
const scrollTop = document.documentElement

src/pages/velog/PostPage.tsx

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
import * as React from 'react';
2-
import styled from 'styled-components';
32
import { RouteComponentProps } from 'react-router';
43
import PostViewer from '../../containers/post/PostViewer';
54

6-
const PostPageBlock = styled.div``;
7-
85
export interface PostPageProps
96
extends RouteComponentProps<{
107
username: string;

src/pages/velog/VelogPage.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import * as React from 'react';
22
import VelogPageTemplate from '../../components/velog/VelogPageTemplate';
33
import { RouteComponentProps, Route } from 'react-router';
44
import ConfigLoader from '../../containers/velog/ConfigLoader';
5-
import PostViewer from '../../containers/post/PostViewer';
65
import PostPage from './PostPage';
76

87
export interface VelogPageProps

yarn.lock

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1027,6 +1027,13 @@
10271027
"@types/keygrip" "*"
10281028
"@types/node" "*"
10291029

1030+
"@types/date-fns@^2.6.0":
1031+
version "2.6.0"
1032+
resolved "https://registry.yarnpkg.com/@types/date-fns/-/date-fns-2.6.0.tgz#b062ca46562002909be0c63a6467ed173136acc1"
1033+
integrity sha1-sGLKRlYgApCb4MY6ZGftFzE2rME=
1034+
dependencies:
1035+
date-fns "*"
1036+
10301037
"@types/estree@*":
10311038
version "0.0.39"
10321039
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f"
@@ -3622,6 +3629,11 @@ data-urls@^1.0.0, data-urls@^1.1.0:
36223629
whatwg-mimetype "^2.2.0"
36233630
whatwg-url "^7.0.0"
36243631

3632+
date-fns@*, date-fns@^1.30.1:
3633+
version "1.30.1"
3634+
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.30.1.tgz#2e71bf0b119153dbb4cc4e88d9ea5acfb50dc05c"
3635+
integrity sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw==
3636+
36253637
date-now@^0.1.4:
36263638
version "0.1.4"
36273639
resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b"

0 commit comments

Comments
 (0)