Skip to content

Commit c816429

Browse files
authored
Merge pull request #66 from BinaryStudioAcademy/feature/infinite-loading
Add infinite loading
2 parents 8e1b15b + fb983f8 commit c816429

File tree

8 files changed

+89
-25
lines changed

8 files changed

+89
-25
lines changed

frontend/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
"moment": "^2.24.0",
1919
"pusher-js": "^4.4.0",
2020
"vue": "^2.6.6",
21+
"vue-infinite-loading": "^2.4.4",
2122
"vue-router": "^3.0.1",
2223
"vuex": "^3.0.1"
2324
},

frontend/src/components/common/TweetPreviewList.vue

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,15 @@
99
/>
1010
</template>
1111
</transition-group>
12+
<infinite-loading @infinite="infiniteHandler">
13+
<span slot="no-more" />
14+
<div slot="no-results" />
15+
</infinite-loading>
1216
</div>
1317
</template>
1418

1519
<script>
20+
import InfiniteLoading from 'vue-infinite-loading';
1621
import TweetPreview from './TweetPreview.vue';
1722
1823
export default {
@@ -22,17 +27,22 @@ export default {
2227
tweets: {
2328
type: Array,
2429
required: true
25-
}
30+
},
2631
},
2732
2833
components: {
2934
TweetPreview,
35+
InfiniteLoading,
3036
},
3137
3238
methods: {
3339
onTweetClick(tweet) {
3440
this.$router.push({ name: 'tweet-page', params: { id: tweet.id } });
3541
},
42+
43+
infiniteHandler($state) {
44+
this.$emit('infinite', $state);
45+
},
3646
},
3747
};
3848
</script>

frontend/src/components/view/feed/FeedContainer.vue

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
</b-button>
1515
</div>
1616

17-
<TweetPreviewList :tweets="tweets" />
17+
<TweetPreviewList :tweets="tweets" @infinite="infiniteHandler" />
1818

1919
<b-modal :active.sync="isNewTweetModalActive" has-modal-card>
2020
<NewTweetForm />
@@ -42,11 +42,14 @@ export default {
4242
4343
data: () => ({
4444
isNewTweetModalActive: false,
45+
page: 1,
4546
}),
4647
4748
async created() {
4849
try {
49-
await this.fetchTweets();
50+
await this.fetchTweets({
51+
page: 1
52+
});
5053
} catch (error) {
5154
this.showErrorMessage(error.message);
5255
}
@@ -80,6 +83,22 @@ export default {
8083
showAddTweetModal() {
8184
this.isNewTweetModalActive = true;
8285
},
86+
87+
async infiniteHandler($state) {
88+
try {
89+
const tweets = await this.fetchTweets({ page: this.page + 1 });
90+
91+
if (tweets.length) {
92+
this.page += 1;
93+
$state.loaded();
94+
} else {
95+
$state.complete();
96+
}
97+
} catch (error) {
98+
this.showErrorMessage(error.message);
99+
$state.complete();
100+
}
101+
},
83102
},
84103
};
85104
</script>

frontend/src/components/view/user/UserContainer.vue

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<template>
22
<div class="user-container">
3-
<TweetPreviewList :tweets="tweets" />
3+
<TweetPreviewList :tweets="tweets" @infinite="infiniteHandler" />
44
<NoContent :show="noContent" title="No tweets yet :)" />
55
</div>
66
</template>
@@ -9,36 +9,67 @@
99
import { mapActions } from 'vuex';
1010
import TweetPreviewList from '@/components/common/TweetPreviewList.vue';
1111
import NoContent from '@/components/common/NoContent.vue';
12+
import showStatusToast from '@/components/mixin/showStatusToast';
1213
1314
export default {
1415
name: 'UserContainer',
1516
17+
mixins: [showStatusToast],
18+
1619
components: {
1720
TweetPreviewList,
1821
NoContent
1922
},
2023
2124
data: () => ({
2225
tweets: [],
23-
noContent: false
26+
page: 1,
27+
noContent: false,
2428
}),
2529
2630
async created() {
2731
try {
28-
this.tweets = await this.fetchTweetsByUserId(this.$route.params.id);
32+
this.tweets = await this.fetchTweetsByUserId({
33+
userId: this.$route.params.id,
34+
params: {
35+
page: 1
36+
}
37+
});
2938
3039
if (!this.tweets.length) {
3140
this.noContent = true;
3241
}
3342
} catch (error) {
34-
console.error(error.message);
43+
this.showErrorMessage(error.message);
3544
}
3645
},
3746
3847
methods: {
3948
...mapActions('tweet', [
4049
'fetchTweetsByUserId',
4150
]),
51+
52+
async infiniteHandler($state) {
53+
try {
54+
const tweets = await this.fetchTweetsByUserId({
55+
userId: this.$route.params.id,
56+
params: {
57+
page: this.page + 1
58+
}
59+
});
60+
61+
if (tweets.length) {
62+
this.tweets.push(...tweets);
63+
this.page += 1;
64+
$state.loaded();
65+
} else {
66+
$state.complete();
67+
}
68+
} catch (error) {
69+
this.showErrorMessage(error.message);
70+
$state.complete();
71+
}
72+
},
4273
},
4374
};
4475
</script>

frontend/src/store/modules/tweet/actions.js

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,28 +11,29 @@ import api from '@/api/Api';
1111
import { tweetMapper } from '@/services/Normalizer';
1212

1313
export default {
14-
async fetchTweets({ commit }) {
14+
async fetchTweets({ commit }, { page }) {
1515
commit(SET_LOADING, true, { root: true });
1616

1717
try {
18-
const data = await api.get('/tweets');
18+
const tweets = await api.get('/tweets', { page });
1919

20-
commit(SET_TWEETS, data);
20+
commit(SET_TWEETS, tweets);
2121
commit(SET_LOADING, false, { root: true });
2222

23-
return Promise.resolve();
23+
return Promise.resolve(
24+
tweets.map(tweetMapper)
25+
);
2426
} catch (error) {
2527
commit(SET_LOADING, false, { root: true });
2628

2729
return Promise.reject(error);
2830
}
2931
},
3032

31-
async fetchTweetsByUserId({ commit }, userId) {
33+
async fetchTweetsByUserId({ commit }, { userId, params }) {
3234
commit(SET_LOADING, true, { root: true });
33-
3435
try {
35-
const tweets = await api.get(`/users/${userId}/tweets`);
36+
const tweets = await api.get(`/users/${userId}/tweets`, params);
3637

3738
commit(SET_LOADING, false, { root: true });
3839

frontend/src/store/modules/tweet/getters.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import moment from 'moment';
33
export default {
44
tweetsSortedByCreatedDate: state => Object.values(state.tweets).sort(
55
(a, b) => (
6-
moment(a.created).isBefore(moment(b.created)) ? 1 : -1
6+
moment(b.created) - moment(a.created)
77
)
88
),
99

frontend/src/store/modules/tweet/mutations.js

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,13 @@ import { tweetMapper } from '@/services/Normalizer';
1111

1212
export default {
1313
[SET_TWEETS]: (state, tweets) => {
14-
let storeTweets = {};
15-
16-
tweets.forEach(tweet => {
17-
storeTweets = {
18-
...storeTweets,
19-
[tweet.id]: tweetMapper(tweet)
20-
};
21-
});
22-
23-
state.tweets = storeTweets;
14+
state.tweets = {
15+
...state.tweets,
16+
...tweets.reduce(
17+
(prev, tweet) => ({ ...prev, [tweet.id]: tweetMapper(tweet) }),
18+
{}
19+
),
20+
};
2421
},
2522

2623
[SET_TWEET_IMAGE]: (state, { id, imageUrl }) => {

frontend/yarn.lock

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8003,6 +8003,11 @@ vue-hot-reload-api@^2.3.0:
80038003
resolved "https://registry.yarnpkg.com/vue-hot-reload-api/-/vue-hot-reload-api-2.3.3.tgz#2756f46cb3258054c5f4723de8ae7e87302a1ccf"
80048004
integrity sha512-KmvZVtmM26BQOMK1rwUZsrqxEGeKiYSZGA7SNWE6uExx8UX/cj9hq2MRV/wWC3Cq6AoeDGk57rL9YMFRel/q+g==
80058005

8006+
vue-infinite-loading@^2.4.4:
8007+
version "2.4.4"
8008+
resolved "https://registry.yarnpkg.com/vue-infinite-loading/-/vue-infinite-loading-2.4.4.tgz#8a9defb9ceeea797c057cb36bdf558a4b2ce409f"
8009+
integrity sha512-eIFBcyKqkivtsDDq7Ee5ybDJVGLxIzU1NcBJCHG7Zx9Ic66QEGzSPs2OPJlGUdtu0/RS7KpUER35ZP/a7FdSOg==
8010+
80068011
vue-loader@^15.7.0:
80078012
version "15.7.0"
80088013
resolved "https://registry.yarnpkg.com/vue-loader/-/vue-loader-15.7.0.tgz#27275aa5a3ef4958c5379c006dd1436ad04b25b3"

0 commit comments

Comments
 (0)