Skip to content

Commit 14a86ff

Browse files
committed
working on temp save pagination
1 parent dbc7f43 commit 14a86ff

File tree

7 files changed

+176
-58
lines changed

7 files changed

+176
-58
lines changed

velog-frontend/src/components/saves/SavePostCard/SavePostCard.js

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,34 @@
11
// @flow
22
import React from 'react';
33
import DeleteIcon from 'react-icons/lib/md/delete';
4+
import { fromNow } from 'lib/common';
5+
import { Link } from 'react-router-dom';
6+
47
import './SavePostCard.scss';
58

6-
type Props = {};
9+
type Props = {
10+
thumbnail: ?string,
11+
id: string,
12+
title: string,
13+
updatedAt: string,
14+
};
715

8-
const SavePostCard = (props: Props) => (
16+
const SavePostCard = ({ id, thumbnail, title, updatedAt }: Props) => (
917
<div className="SavePostCard">
10-
<div className="img-wrapper">
11-
<img
12-
src="https://images.velog.io/post-images/velopert/1d26a150-6747-11e8-9dff-1b161279fc07/goodb.png"
13-
alt="thumbnail"
14-
/>
15-
</div>
18+
{thumbnail && (
19+
<div className="img-wrapper">
20+
<img
21+
src="https://images.velog.io/post-images/velopert/1d26a150-6747-11e8-9dff-1b161279fc07/goodb.png"
22+
alt="thumbnail"
23+
/>
24+
</div>
25+
)}
1626
<div className="white-area">
1727
<div className="post-info">
18-
<h3>제목이 있다리</h3>
19-
<div className="date">4 초 전</div>
28+
<h3>
29+
<Link to={`/write?edit_id=${id}`}>{title}</Link>
30+
</h3>
31+
<div className="date">{fromNow(updatedAt)}</div>
2032
</div>
2133
<button className="remove-button">
2234
<DeleteIcon />

velog-frontend/src/components/saves/SavePostCard/SavePostCard.scss

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
@import 'utils';
22
.SavePostCard {
33
border: 1px solid $oc-gray-3;
4+
color: $oc-gray-9;
45
font-family: initial;
56
.img-wrapper {
67
padding-top: 52.63%;
@@ -24,6 +25,12 @@
2425
margin: 0;
2526
font-size: 1.5rem;
2627
font-weight: 500;
28+
a {
29+
&:hover {
30+
color: $oc-violet-5;
31+
text-decoration: underline;
32+
}
33+
}
2734
}
2835
.date {
2936
color: $oc-gray-7;
Lines changed: 17 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,25 @@
11
// @flow
22
import React from 'react';
3+
import type { PostItem } from 'store/modules/listing';
34
import './SavePostCardList.scss';
45
import SavePostCard from '../SavePostCard';
56

6-
type Props = {};
7+
type Props = {
8+
posts: ?(PostItem[]),
9+
};
710

8-
const SavePostCardList = (props: Props) => (
9-
<div className="SavePostCardList">
10-
<SavePostCard />
11-
<SavePostCard />
12-
<SavePostCard />
13-
<SavePostCard />
14-
<SavePostCard />
15-
<SavePostCard />
16-
<SavePostCard />
17-
<SavePostCard />
18-
<SavePostCard />
19-
<SavePostCard />
20-
<SavePostCard />
21-
<SavePostCard />
22-
<SavePostCard />
23-
<SavePostCard />
24-
<SavePostCard />
25-
<SavePostCard />
26-
<SavePostCard />
27-
<SavePostCard />
28-
<SavePostCard />
29-
</div>
30-
);
11+
const SavePostCardList = ({ posts }: Props) => {
12+
if (!posts) return null;
13+
const postList = posts.map(post => (
14+
<SavePostCard
15+
key={post.id}
16+
id={post.id}
17+
title={post.title}
18+
thumbnail={post.thumbnail}
19+
updatedAt={post.updated_at}
20+
/>
21+
));
22+
return <div className="SavePostCardList">{postList}</div>;
23+
};
3124

3225
export default SavePostCardList;
Lines changed: 60 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,72 @@
11
// @flow
22
import React, { Component } from 'react';
3-
import * as actions from 'store/actionCreators';
3+
import { ListingActions } from 'store/actionCreators';
44
import type { State } from 'store';
55
import { connect } from 'react-redux';
66
import SavePostCardList from 'components/saves/SavePostCardList/SavePostCardList';
7+
import type { PostItem } from 'store/modules/listing';
8+
import throttle from 'lodash/throttle';
9+
import { getScrollBottom, preventStickBottom } from 'lib/common';
710

8-
type Props = {};
11+
type Props = {
12+
username: ?string,
13+
posts: ?(PostItem[]),
14+
prefetching: boolean,
15+
loading: boolean,
16+
hasEnded: boolean,
17+
};
918

1019
class SavePostCardListContainer extends Component<Props> {
20+
prevCursor: ?string = null;
21+
22+
initialize = async () => {
23+
if (!this.props.username) return;
24+
try {
25+
await ListingActions.getTempPosts({
26+
username: this.props.username,
27+
});
28+
this.prefetch();
29+
} catch (e) {
30+
console.log(e);
31+
}
32+
};
33+
34+
componentDidMount() {
35+
this.initialize();
36+
window.addEventListener('scroll', this.onScroll);
37+
}
38+
39+
componentWillUnmount() {
40+
window.removeEventListener('scroll', this.onScroll);
41+
}
42+
43+
componentDidUpdate(prevProps, prevState) {
44+
if (!prevProps.username && this.props.username) {
45+
this.initialize();
46+
}
47+
}
48+
49+
prefetch = async () => {};
50+
51+
onScroll = throttle(() => {
52+
const scrollBottom = getScrollBottom();
53+
if (scrollBottom > 1000) return;
54+
this.prefetch();
55+
}, 250);
56+
1157
render() {
12-
return <SavePostCardList />;
58+
const { posts } = this.props;
59+
return <SavePostCardList posts={posts} />;
1360
}
1461
}
1562

16-
export default connect((state: State) => ({}), () => ({}))(SavePostCardListContainer);
63+
export default connect(
64+
({ user, listing, pender }: State) => ({
65+
username: user.user && user.user.username,
66+
posts: listing.temp.posts,
67+
prefetching: pender.pending['listing/PREFETCH_TEMP_POSTS'],
68+
loading: pender.pending['listing/GET_TEMP_POSTS'],
69+
hasEnded: listing.temp.end,
70+
}),
71+
() => ({}),
72+
)(SavePostCardListContainer);

velog-frontend/src/lib/api/posts/index.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,3 +100,16 @@ export const getPublicPostsByTag = (payload: GetPublicPostsByTagPayload) => {
100100
const query = queryString.stringify(payload);
101101
return axios.get(`/posts/public?${query}`);
102102
};
103+
104+
export type GetTempPostsPayload = {
105+
username: string,
106+
cursor?: string,
107+
};
108+
109+
export const getTempPosts = ({ username, cursor }: GetTempPostsPayload) => {
110+
const query = queryString.stringify({
111+
is_temp: true,
112+
cursor,
113+
});
114+
return axios.get(`/posts/@${username}?${query}`);
115+
};

velog-frontend/src/lib/common.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// @flow
22
import { pender } from 'redux-pender';
33
import type { $AxiosXHR, $AxiosError } from 'axios';
4+
import moment from 'moment';
45

56
export const pressedEnter = (fn: () => void) => (e: KeyboardEvent) => {
67
if (e.key === 'Enter') {
@@ -74,3 +75,13 @@ export const escapeForUrl = (text: string): string => {
7475
.replace(/ /g, '-')
7576
.replace(/--+/g, '-');
7677
};
78+
79+
export const fromNow = (date: string) => {
80+
const now = new Date();
81+
const givenDate = new Date(date);
82+
const lessThanWeek = now - givenDate < 1000 * 60 * 60 * 24 * 7;
83+
if (lessThanWeek) {
84+
return moment(date).fromNow();
85+
}
86+
return moment(date).format('YYYY년 M월 D일');
87+
};

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

Lines changed: 46 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ const PREFETCH_TRENDING_POSTS = 'listing/PREFETCH_TRENDING_POSTS';
1515
const GET_TAG_POSTS = 'listing/GET_TAG_POSTS';
1616
const PREFETCH_TAG_POSTS = 'listing/PREFETCH_TAG_POSTS';
1717
const CLEAR_TAG_POSTS = 'listing/CLEAR_TAG_POSTS';
18+
const GET_TEMP_POSTS = 'listing/GET_TEMP_POSTS';
19+
const PREFETCH_TEMP_POSTS = 'listing/PREFETCH_TEMP_POSTS';
1820

1921
// potential improvement :: remove duplicates
2022
export const actionCreators = {
@@ -29,6 +31,8 @@ export const actionCreators = {
2931
getTagPosts: createAction(GET_TAG_POSTS, PostsAPI.getPublicPostsByTag),
3032
prefetchTagPosts: createAction(PREFETCH_TAG_POSTS, PostsAPI.getPublicPostsByTag),
3133
clearTagPosts: createAction(CLEAR_TAG_POSTS),
34+
getTempPosts: createAction(GET_TEMP_POSTS, PostsAPI.getTempPosts),
35+
prefetchTempPosts: createAction(PREFETCH_TEMP_POSTS, PostsAPI.getTempPosts),
3236
};
3337

3438
export type PostItem = {
@@ -63,6 +67,7 @@ export type Listing = {
6367
recent: ListingSet,
6468
user: ListingSet,
6569
tag: ListingSet,
70+
temp: ListingSet,
6671
};
6772

6873
type RevealPrefetchedAction = ActionType<typeof actionCreators.revealPrefetched>;
@@ -83,31 +88,23 @@ export interface ListingActionCreators {
8388
prefetchTrendingPosts(cursor: string): any;
8489
getTagPosts(payload: PostsAPI.GetPublicPostsByTagPayload): any;
8590
prefetchTagPosts(payload: PostsAPI.GetPublicPostsByTagPayload): any;
91+
getTempPosts(payload: PostsAPI.GetTempPostsPayload): any;
8692
clearTagPosts(): any;
8793
clearUserPosts(): any;
8894
}
8995

96+
const initialListingSet = {
97+
posts: null,
98+
prefetched: null,
99+
end: false,
100+
};
101+
90102
const initialState: Listing = {
91-
trending: {
92-
posts: null,
93-
prefetched: null,
94-
end: false,
95-
},
96-
recent: {
97-
posts: null,
98-
prefetched: null,
99-
end: false,
100-
},
101-
user: {
102-
posts: null,
103-
prefetched: null,
104-
end: false,
105-
},
106-
tag: {
107-
posts: null,
108-
prefetched: null,
109-
end: false,
110-
},
103+
trending: initialListingSet,
104+
recent: initialListingSet,
105+
user: initialListingSet,
106+
tag: initialListingSet,
107+
temp: initialListingSet,
111108
};
112109

113110
const reducer = handleActions(
@@ -246,4 +243,33 @@ export default applyPenders(reducer, [
246243
});
247244
},
248245
},
246+
{
247+
type: GET_TEMP_POSTS,
248+
onPending: (state: Listing) => {
249+
return produce(state, (draft) => {
250+
draft.temp.end = false;
251+
draft.temp.prefetched = null;
252+
});
253+
},
254+
onSuccess: (state: Listing, action: PostsResponseAction) => {
255+
return produce(state, (draft) => {
256+
draft.temp.posts = action.payload.data;
257+
if (action.payload.data.length < 20) {
258+
draft.temp.end = true;
259+
}
260+
});
261+
},
262+
},
263+
{
264+
type: PREFETCH_TEMP_POSTS,
265+
onSuccess: (state: Listing, action: PostsResponseAction) => {
266+
const { data } = action.payload;
267+
return produce(state, (draft) => {
268+
draft.temp.prefetched = data;
269+
if (data && data.length === 0) {
270+
draft.temp.end = true;
271+
}
272+
});
273+
},
274+
},
249275
]);

0 commit comments

Comments
 (0)