Skip to content

Commit 9473be2

Browse files
committed
implemented router action and post delete/update feature
1 parent 2acb3b8 commit 9473be2

File tree

6 files changed

+127
-14
lines changed

6 files changed

+127
-14
lines changed

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

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,21 @@
11
// @flow
22
import React from 'react';
3+
import { Link } from 'react-router-dom';
34
import './PostActionButtons.scss';
45

5-
type Props = {};
6+
type Props = {
7+
id: string,
8+
onAskRemove: () => void,
9+
};
610

7-
const PostActionButtons = (props: Props) => (
11+
const PostActionButtons = ({ id, onAskRemove }: Props) => (
812
<div className="PostActionButtons">
9-
<button className="btn">수정</button>
10-
<button className="btn">삭제</button>
13+
<Link to={`/write?edit_id=${id}`} className="btn">
14+
수정
15+
</Link>
16+
<button className="btn" onClick={onAskRemove}>
17+
삭제
18+
</button>
1119
</div>
1220
);
1321

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

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import PostActionButtons from '../PostActionButtons';
99
import './PostHead.scss';
1010

1111
type Props = {
12+
id: string,
1213
title: string,
1314
categories: Categories,
1415
user: {
@@ -21,9 +22,20 @@ type Props = {
2122
liked: boolean,
2223
ownPost: boolean,
2324
onToggleLike: () => void,
25+
onAskRemove: () => void,
2426
};
2527

26-
const PostHead = ({ title, categories, user, likes, liked, ownPost, onToggleLike }: Props) => {
28+
const PostHead = ({
29+
id,
30+
title,
31+
categories,
32+
user,
33+
likes,
34+
liked,
35+
ownPost,
36+
onToggleLike,
37+
onAskRemove,
38+
}: Props) => {
2739
const userLink = `/@${user.username}`;
2840

2941
return (
@@ -46,7 +58,7 @@ const PostHead = ({ title, categories, user, likes, liked, ownPost, onToggleLike
4658
<PostLikeButton onClick={onToggleLike} liked={liked} likes={likes} />
4759
</div>
4860
<div className="separator" />
49-
{ownPost && <PostActionButtons />}
61+
{ownPost && <PostActionButtons id={id} onAskRemove={onAskRemove} />}
5062
</div>
5163
);
5264
};

velog-frontend/src/containers/base/Core.js

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,23 @@
11
// @flow
22
import React, { Component, Fragment } from 'react';
3-
import { UserActions, BaseActions } from 'store/actionCreators';
3+
import { UserActions, BaseActions, CommonActions } from 'store/actionCreators';
44
import { connect } from 'react-redux';
55
import type { State } from 'store';
66
import type { UserData } from 'store/modules/user';
77
import storage from 'lib/storage';
88
import NanoBar from 'components/common/NanoBar';
99
import throttle from 'lodash/throttle';
10+
import { withRouter, type ContextRouter } from 'react-router-dom';
11+
1012
import FullscreenLoaderContainer from './FullscreenLoaderContainer';
1113

1214
type Props = {
1315
user: ?UserData,
14-
};
16+
} & ContextRouter;
1517

1618
class Core extends Component<Props> {
19+
unlisten = null;
20+
1721
checkUser = async () => {
1822
const storedUser = storage.get('__velog_user__');
1923
if (!storedUser) {
@@ -52,9 +56,24 @@ class Core extends Component<Props> {
5256
// TODO
5357
};
5458

59+
listenHistory = () => {
60+
const { history } = this.props;
61+
this.unlisten = history.listen((location, type) => {
62+
CommonActions.changeRoute({
63+
type,
64+
...location,
65+
});
66+
});
67+
};
68+
5569
componentDidMount() {
5670
this.initialize();
5771
this.integrateAxiosProgressbar();
72+
this.listenHistory();
73+
}
74+
75+
componentWillUnmount() {
76+
if (this.unlisten) this.unlisten();
5877
}
5978

6079
render() {
@@ -72,4 +91,4 @@ export default connect(
7291
user: user.user,
7392
}),
7493
() => ({}),
75-
)(Core);
94+
)(withRouter(Core));

velog-frontend/src/containers/post/PostViewer.js

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@ import React, { Component, Fragment } from 'react';
33
import PostHead from 'components/post/PostHead';
44
import PostContent from 'components/post/PostContent';
55
import PostTags from 'components/post/PostTags';
6-
import { PostsActions } from 'store/actionCreators';
6+
import { PostsActions, CommonActions } from 'store/actionCreators';
77
import { connect } from 'react-redux';
88
import type { State } from 'store';
99
import type { PostData, TocItem } from 'store/modules/posts';
1010
import PostToc from 'components/post/PostToc';
11+
import QuestionModal from 'components/common/QuestionModal/QuestionModal';
12+
import { withRouter, type ContextRouter, type Location } from 'react-router-dom';
1113

1214
type Props = {
1315
username: ?string,
@@ -17,7 +19,9 @@ type Props = {
1719
activeHeading: ?string,
1820
likeInProcess: boolean,
1921
currentUsername: ?string,
20-
};
22+
askRemove: boolean,
23+
routerHistory: Location[],
24+
} & ContextRouter;
2125

2226
class PostViewer extends Component<Props> {
2327
initialize = async () => {
@@ -52,19 +56,40 @@ class PostViewer extends Component<Props> {
5256
}
5357
};
5458

59+
onToggleAskRemove = () => {
60+
PostsActions.toggleAskRemove();
61+
};
62+
63+
onConfirmRemove = async () => {
64+
const { post, history, routerHistory } = this.props;
65+
PostsActions.toggleAskRemove();
66+
if (!post) return;
67+
try {
68+
CommonActions.removePost(post.id);
69+
} catch (e) {
70+
console.log(e);
71+
}
72+
if (routerHistory.length === 0) {
73+
history.push('/');
74+
return;
75+
}
76+
history.goBack();
77+
};
78+
5579
componentDidMount() {
5680
this.initialize();
5781
}
5882

5983
render() {
60-
const { post, toc, activeHeading, username, currentUsername } = this.props;
84+
const { post, toc, activeHeading, username, currentUsername, askRemove } = this.props;
6185
const { onSetToc, onActivateHeading } = this;
6286
if (!post) return null;
6387

6488
return (
6589
<Fragment>
6690
<PostToc toc={toc} activeHeading={activeHeading} />
6791
<PostHead
92+
id={post.id}
6893
title={post.title}
6994
tags={post.tags}
7095
categories={post.categories}
@@ -73,6 +98,7 @@ class PostViewer extends Component<Props> {
7398
liked={post.liked}
7499
onToggleLike={this.onToggleLike}
75100
ownPost={currentUsername === username}
101+
onAskRemove={this.onToggleAskRemove}
76102
/>
77103
<PostContent
78104
thumbnail={post.thumbnail}
@@ -81,18 +107,28 @@ class PostViewer extends Component<Props> {
81107
onActivateHeading={onActivateHeading}
82108
/>
83109
<PostTags tags={post.tags} />
110+
<QuestionModal
111+
open={askRemove}
112+
title="포스트 삭제"
113+
description="이 포스트를 정말로 삭제하시겠습니까?"
114+
confirmText="삭제"
115+
onConfirm={this.onConfirmRemove}
116+
onCancel={this.onToggleAskRemove}
117+
/>
84118
</Fragment>
85119
);
86120
}
87121
}
88122

89123
export default connect(
90-
({ posts, pender, user }: State) => ({
124+
({ posts, pender, user, common }: State) => ({
91125
currentUsername: user.user && user.user.username,
92126
post: posts.post,
93127
toc: posts.toc,
94128
activeHeading: posts.activeHeading,
95129
likeInProcess: pender.pending['posts/LIKE'] || pender.pending['posts/UNLIKE'],
130+
askRemove: posts.askRemove,
131+
routerHistory: common.router.history,
96132
}),
97133
() => ({}),
98-
)(PostViewer);
134+
)(withRouter(PostViewer));

velog-frontend/src/store/modules/common.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,32 +4,38 @@ import { applyPenders, type GenericResponseAction } from 'lib/common';
44
import * as CommonAPI from 'lib/api/common';
55
import produce from 'immer';
66
import * as PostsAPI from 'lib/api/posts';
7+
import { type Location } from 'react-router-dom';
78

89
const GET_TAGS = 'common/GET_TAGS';
910
const SET_TAG_INFO = 'common/SET_TAG_INFO';
1011
const GET_TAG_INFO = 'common/GET_TAG_INFO';
1112
const ASK_REMOVE = 'common/saves/ASK_REMOVE';
1213
const CLOSE_REMOVE = 'common/saves/CLOSE_REMOVE';
1314
const REMOVE_POST = 'common/saves/REMOVE_POST';
15+
const CHANGE_ROUTE = 'common/router/CHANGE_ROUTE';
1416

1517
export type TagData = {
1618
name: string,
1719
posts_count: number,
1820
};
1921

22+
export type HistoryPayload = Location & { type: string };
23+
2024
export const actionCreators = {
2125
getTags: createAction(GET_TAGS, CommonAPI.getTags, meta => meta),
2226
setTagInfo: createAction(SET_TAG_INFO, (info: ?TagData) => info),
2327
getTagInfo: createAction(GET_TAG_INFO, CommonAPI.getTagInfo),
2428
askRemove: createAction(ASK_REMOVE, (postId: string) => postId),
2529
closeRemove: createAction(CLOSE_REMOVE),
2630
removePost: createAction(REMOVE_POST, PostsAPI.deletePost),
31+
changeRoute: createAction(CHANGE_ROUTE, (payload: HistoryPayload) => payload),
2732
};
2833

2934
type GetTagsResponseAction = GenericResponseAction<TagData[], string>;
3035
type GetTagInfoResponseAction = GenericResponseAction<TagData, string>;
3136
type SetTagInfoAction = ActionType<typeof actionCreators.setTagInfo>;
3237
type AskRemoveAction = ActionType<typeof actionCreators.askRemove>;
38+
type ChangeRouteAction = ActionType<typeof actionCreators.changeRoute>;
3339

3440
export type CommonState = {
3541
tags: {
@@ -42,6 +48,9 @@ export type CommonState = {
4248
removeId: ?string,
4349
ask: boolean,
4450
},
51+
router: {
52+
history: Location[],
53+
},
4554
};
4655

4756
const initialState: CommonState = {
@@ -55,6 +64,9 @@ const initialState: CommonState = {
5564
removeId: null,
5665
ask: false,
5766
},
67+
router: {
68+
history: [],
69+
},
5870
};
5971

6072
const reducer = handleActions(
@@ -75,6 +87,20 @@ const reducer = handleActions(
7587
draft.saves.ask = false;
7688
});
7789
},
90+
[CHANGE_ROUTE]: (state, { payload }: ChangeRouteAction) => {
91+
return produce(state, (draft) => {
92+
const { type, ...rest } = payload;
93+
if (type === 'PUSH') {
94+
draft.router.history.push(rest);
95+
}
96+
if (type === 'POP') {
97+
draft.router.history.pop();
98+
}
99+
if (type === 'REPLACE') {
100+
draft.router.history[draft.router.history.length - 1] = payload;
101+
}
102+
});
103+
},
78104
},
79105
initialState,
80106
);

velog-frontend/src/store/modules/posts.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ const READ_SUBCOMMENTS = 'posts/READ_SUBCOMMENTS';
2222
const LIKE = 'posts/LIKE';
2323
const UNLIKE = 'posts/UNLIKE';
2424
const GET_LIKES_COUNT = 'posts/GET_LIKES_COUNT';
25+
const TOGGLE_ASK_REMOVE = 'posts/TOGGLE_ASK_REMOVE';
2526

2627
export interface PostsActionCreators {
2728
readPost(payload: PostsAPI.ReadPostPayload): any;
@@ -33,6 +34,7 @@ export interface PostsActionCreators {
3334
like(postId: string): any;
3435
unlike(postId: string): any;
3536
getLikesCount(postId: string): any;
37+
toggleAskRemove(): any;
3638
}
3739

3840
export const actionCreators = {
@@ -45,11 +47,13 @@ export const actionCreators = {
4547
like: createAction(LIKE, LikesAPI.like),
4648
unlike: createAction(UNLIKE, LikesAPI.unlike),
4749
getLikesCount: createAction(GET_LIKES_COUNT, LikesAPI.getLikesCount),
50+
toggleAskRemove: createAction(TOGGLE_ASK_REMOVE),
4851
};
4952

5053
type SetTocAction = ActionType<typeof actionCreators.setToc>;
5154
type ActivateHeadingAction = ActionType<typeof actionCreators.activateHeading>;
5255

56+
5357
export type Categories = { id: string, name: string, url_slug: string }[];
5458
export type Comment = {
5559
id: string,
@@ -95,6 +99,7 @@ export type Posts = {
9599
activeHeading: ?string,
96100
comments: ?(Comment[]),
97101
subcommentsMap: SubcommentsMap,
102+
askRemove: boolean,
98103
};
99104

100105
const initialState: Posts = {
@@ -103,6 +108,7 @@ const initialState: Posts = {
103108
activeHeading: null,
104109
comments: null,
105110
subcommentsMap: {},
111+
askRemove: false,
106112
};
107113

108114
const reducer = handleActions(
@@ -117,6 +123,12 @@ const reducer = handleActions(
117123
draft.activeHeading = action.payload;
118124
});
119125
},
126+
[TOGGLE_ASK_REMOVE]: (state) => {
127+
return {
128+
...state,
129+
askRemove: !state.askRemove,
130+
};
131+
},
120132
},
121133
initialState,
122134
);

0 commit comments

Comments
 (0)