Skip to content
This repository was archived by the owner on Mar 3, 2020. It is now read-only.

Commit 04bb244

Browse files
committed
Implement mobile share buttons, and copyURL feature
1 parent 2e35b63 commit 04bb244

File tree

9 files changed

+249
-22
lines changed

9 files changed

+249
-22
lines changed

velog-frontend/package.json

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -77,20 +77,21 @@
7777
"whatwg-fetch": "2.0.3"
7878
},
7979
"scripts": {
80-
"finalize":
81-
"yarn build && yarn build:server && yarn deploy && yarn invalidate",
80+
"finalize": "yarn build && yarn build:server && yarn deploy && yarn invalidate",
8281
"start": "node scripts/start.js",
8382
"build": "node scripts/build.js",
84-
"build:server":
85-
"node scripts/build.server.js && cp ../velog-ssr/src/ssr/render.js ../velog-ssr/dist/ssr/render.js && cp ./build/asset-manifest.json ../velog-ssr/",
83+
"build:server": "node scripts/build.server.js && cp ../velog-ssr/src/ssr/render.js ../velog-ssr/dist/ssr/render.js && cp ./build/asset-manifest.json ../velog-ssr/",
8684
"test": "node scripts/test.js --env=jsdom",
8785
"deploy": "aws s3 sync ./build s3://cdn.velog.io/",
88-
"invalidate":
89-
"aws cloudfront create-invalidation --distribution-id E1M9D68QZ0CD1F --paths / /index.html /error.html /service-worker.js /manifest.json /favicon.ico"
86+
"invalidate": "aws cloudfront create-invalidation --distribution-id E1M9D68QZ0CD1F --paths / /index.html /error.html /service-worker.js /manifest.json /favicon.ico"
9087
},
9188
"jest": {
92-
"collectCoverageFrom": ["src/**/*.{js,jsx,mjs}"],
93-
"setupFiles": ["<rootDir>/config/polyfills.js"],
89+
"collectCoverageFrom": [
90+
"src/**/*.{js,jsx,mjs}"
91+
],
92+
"setupFiles": [
93+
"<rootDir>/config/polyfills.js"
94+
],
9495
"testMatch": [
9596
"<rootDir>/src/**/__tests__/**/*.{js,jsx,mjs}",
9697
"<rootDir>/src/**/?(*.)(spec|test).{js,jsx,mjs}"
@@ -100,10 +101,11 @@
100101
"transform": {
101102
"^.+\\.(js|jsx|mjs)$": "<rootDir>/node_modules/babel-jest",
102103
"^.+\\.css$": "<rootDir>/config/jest/cssTransform.js",
103-
"^(?!.*\\.(js|jsx|mjs|css|json)$)":
104-
"<rootDir>/config/jest/fileTransform.js"
104+
"^(?!.*\\.(js|jsx|mjs|css|json)$)": "<rootDir>/config/jest/fileTransform.js"
105105
},
106-
"transformIgnorePatterns": ["[/\\\\]node_modules[/\\\\].+\\.(js|jsx|mjs)$"],
106+
"transformIgnorePatterns": [
107+
"[/\\\\]node_modules[/\\\\].+\\.(js|jsx|mjs)$"
108+
],
107109
"moduleNameMapper": {
108110
"^react-native$": "react-native-web"
109111
},
@@ -118,7 +120,9 @@
118120
]
119121
},
120122
"babel": {
121-
"presets": ["react-app"]
123+
"presets": [
124+
"react-app"
125+
]
122126
},
123127
"devDependencies": {
124128
"eslint-config-airbnb": "^16.1.0",

velog-frontend/src/components/post/PostHead/PostHead.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { fromNow, resizeImage } from 'lib/common';
99
import LockIcon from 'react-icons/lib/md/lock';
1010
import PostActionButtons from '../PostActionButtons';
1111
import './PostHead.scss';
12+
import PostMobileShare from '../PostMobileShare/PostMobileShare';
1213

1314
type Props = {
1415
id: string,
@@ -27,8 +28,9 @@ type Props = {
2728
onAskRemove: () => void,
2829
date: string,
2930
logged: boolean,
30-
31+
url: string,
3132
isPrivate: boolean,
33+
informCopy: () => void,
3234
};
3335

3436
const PostHead = ({
@@ -44,6 +46,8 @@ const PostHead = ({
4446
onAskRemove,
4547
logged,
4648
isPrivate,
49+
url,
50+
informCopy,
4751
}: Props) => {
4852
const userLink = `/@${user.username}`;
4953

@@ -69,6 +73,7 @@ const PostHead = ({
6973
<div className="date-and-likes">
7074
<div className="date">{fromNow(date)}</div>
7175
<div className="placeholder" />
76+
<PostMobileShare url={url} title={title} username={user.username} informCopy={informCopy} />
7277
<PostLikeButton onClick={onToggleLike} liked={liked} likes={likes} disabled={!logged} />
7378
</div>
7479
<div className="separator" />

velog-frontend/src/components/post/PostHead/PostHead.scss

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
@import 'utils';
1+
@import "utils";
22
.PostHead {
33
&.placeholder {
44
.gray-block {
@@ -38,7 +38,7 @@
3838
}
3939
margin-top: 4rem;
4040
font-size: 1rem;
41-
@include media('<medium') {
41+
@include media("<medium") {
4242
font-size: 0.875rem;
4343
margin-top: 0rem;
4444
}
@@ -64,7 +64,7 @@
6464
margin-bottom: 0.5rem;
6565
}
6666
h1 {
67-
font-family: 'Noto Serif KR', sans-serif;
67+
font-family: "Noto Serif KR", sans-serif;
6868
margin: 0;
6969
font-size: 2.5em;
7070
line-height: 3.2rem;
@@ -74,7 +74,7 @@
7474
color: $oc-gray-6;
7575
}
7676
.userinfo {
77-
@include media('<medium') {
77+
@include media("<medium") {
7878
background: $oc-gray-0;
7979
margin-top: -0.25rem;
8080
border-top: 1px solid $oc-gray-2;
@@ -122,7 +122,8 @@
122122
.placeholder {
123123
flex: 1;
124124
}
125-
.PostLikeButton {
125+
.PostLikeButton,
126+
.PostMobileShare {
126127
@include media(">=large") {
127128
display: none;
128129
}
@@ -135,4 +136,4 @@
135136
margin-top: 2em;
136137
margin-bottom: 3em;
137138
}
138-
}
139+
}

velog-frontend/src/components/post/PostLeftSticker/PostLeftSticker.js

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,21 @@ import TwitterIcon from 'react-icons/lib/fa/twitter';
99
import ExitIcon from 'react-icons/lib/md/close';
1010
import Tooltip from 'react-tooltip';
1111
import withClickOutside from 'react-onclickoutside';
12+
import LinkIcon from 'react-icons/lib/md/link';
1213

1314
import cx from 'classnames';
1415
import './PostLeftSticker.scss';
15-
import { shareFacebook, shareTwitter } from '../../../lib/share';
16+
import { shareFacebook, shareTwitter, copyText } from '../../../lib/share';
1617

1718
type Props = {
1819
likes: number,
1920
liked: boolean,
2021
onToggleLike: () => void,
22+
informCopy: () => void,
2123
logged: boolean,
2224
url: string,
2325
title: string,
26+
username: string,
2427
};
2528

2629
type State = {
@@ -64,7 +67,15 @@ class PostLeftSticker extends Component<Props, State> {
6467
};
6568
onTwitterShare = () => {
6669
this.onToggleShareButton();
67-
shareTwitter(`https://velog.io${this.props.url}`, this.props.title);
70+
shareTwitter(
71+
`https://velog.io${this.props.url}`,
72+
`${this.props.username}님이 작성하신 "${this.props.title}" 포스트를 읽어보세요.`,
73+
);
74+
};
75+
onCopy = () => {
76+
this.onToggleShareButton();
77+
copyText(`https://velog.io${this.props.url}`);
78+
this.props.informCopy();
6879
};
6980
componentDidUpdate(prevProps: Props) {
7081
if (!prevProps.liked && this.props.liked) {
@@ -105,6 +116,9 @@ class PostLeftSticker extends Component<Props, State> {
105116
<button className="circle share" onClick={this.onTwitterShare}>
106117
<TwitterIcon />
107118
</button>
119+
<button className="circle share" onClick={this.onCopy}>
120+
<LinkIcon />
121+
</button>
108122
</Fragment>
109123
)}
110124
</div>
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
// @flow
2+
import React, { Component, Fragment } from 'react';
3+
import ShareIcon from 'react-icons/lib/md/share';
4+
import ExitIcon from 'react-icons/lib/md/close';
5+
import FacebookIcon from 'react-icons/lib/fa/facebook-official';
6+
import TwitterIcon from 'react-icons/lib/fa/twitter';
7+
import LinkIcon from 'react-icons/lib/md/link';
8+
import { shareFacebook, shareTwitter, copyText } from 'lib/share';
9+
import cx from 'classnames';
10+
11+
import './PostMobileShare.scss';
12+
13+
type Props = {
14+
url: string,
15+
title: string,
16+
username: string,
17+
informCopy: () => void,
18+
};
19+
type State = {
20+
open: boolean,
21+
animating: boolean,
22+
};
23+
24+
class PostMobileShare extends Component<Props, State> {
25+
animateTimeoutId = null;
26+
27+
state = {
28+
open: false,
29+
animating: false,
30+
};
31+
32+
onToggle = () => {
33+
this.setState({
34+
open: !this.state.open,
35+
animating: true,
36+
});
37+
if (this.animateTimeoutId) {
38+
clearTimeout(this.animateTimeoutId);
39+
}
40+
setTimeout(() => {
41+
this.setState({
42+
animating: false,
43+
});
44+
}, 150);
45+
};
46+
47+
onFacebookShare = () => {
48+
this.onToggle();
49+
shareFacebook(`https://velog.io${this.props.url}`);
50+
};
51+
52+
onTwitterShare = () => {
53+
this.onToggle();
54+
shareTwitter(
55+
`https://velog.io${this.props.url}`,
56+
`${this.props.username}님이 작성하신 "${this.props.title}" 포스트를 읽어보세요.`,
57+
);
58+
};
59+
60+
onCopy = () => {
61+
this.onToggle();
62+
copyText(`http://velog.io${this.props.url}`);
63+
this.props.informCopy();
64+
};
65+
66+
componentWillUnmount() {
67+
if (this.animateTimeoutId) {
68+
clearTimeout(this.animateTimeoutId);
69+
}
70+
}
71+
72+
render() {
73+
const { open, animating } = this.state;
74+
75+
const transition = (() => {
76+
if (!animating) return '';
77+
if (open) return 'appear';
78+
return 'disappear';
79+
})();
80+
81+
return (
82+
<div className="PostMobileShare">
83+
{(open || animating) && (
84+
<div className={cx('buttons', transition)}>
85+
<button onClick={this.onFacebookShare}>
86+
<FacebookIcon />
87+
</button>
88+
<button onClick={this.onTwitterShare}>
89+
<TwitterIcon />
90+
</button>
91+
<button onClick={this.onCopy}>
92+
<LinkIcon />
93+
</button>
94+
</div>
95+
)}
96+
<button className={cx({ open: this.state.open })} onClick={this.onToggle}>
97+
{this.state.open ? <ExitIcon /> : <ShareIcon />}
98+
</button>
99+
</div>
100+
);
101+
}
102+
}
103+
104+
export default PostMobileShare;
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
@import "utils";
2+
3+
@keyframes slideFromRight {
4+
0% {
5+
opacity: 0;
6+
transform: translateX(24px);
7+
}
8+
75% {
9+
opacity: 1;
10+
transform: translateX(-8px);
11+
}
12+
100% {
13+
opacity: 1;
14+
transform: translateX(0px);
15+
}
16+
}
17+
18+
@keyframes slideToRight {
19+
0% {
20+
opacity: 1;
21+
transform: translateX(0px);
22+
}
23+
25% {
24+
opacity: 1;
25+
transform: translateX(-8px);
26+
}
27+
100% {
28+
opacity: 0;
29+
transform: translateX(24px);
30+
}
31+
}
32+
33+
.PostMobileShare {
34+
display: flex;
35+
.buttons {
36+
display: flex;
37+
&.appear {
38+
animation: slideFromRight 0.15s ease-in;
39+
animation-fill-mode: forwards;
40+
}
41+
&.disappear {
42+
animation: slideToRight 0.15s ease-in;
43+
animation-fill-mode: forwards;
44+
}
45+
}
46+
button {
47+
background: white;
48+
width: 2.25rem;
49+
height: 2.25rem;
50+
border: 1px solid $oc-gray-6;
51+
color: $oc-gray-6;
52+
border-radius: 1.125rem;
53+
display: flex;
54+
align-items: center;
55+
justify-content: center;
56+
margin-right: 0.5rem;
57+
cursor: pointer;
58+
font-size: 1.125rem;
59+
&:hover {
60+
border: 1px solid $oc-gray-9;
61+
color: $oc-gray-9;
62+
}
63+
&.open {
64+
color: $oc-red-5;
65+
border: 1px solid $oc-red-5;
66+
&:hover {
67+
border: 1px solid $oc-red-7;
68+
color: $oc-red-7;
69+
}
70+
}
71+
}
72+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// @flow
2+
import PostMobileShare from './PostMobileShare';
3+
4+
export default PostMobileShare;

0 commit comments

Comments
 (0)