mirror of
https://github.com/Ocelot-Social-Community/Ocelot-Social.git
synced 2025-12-13 07:46:06 +00:00
Merge pull request #2248 from Human-Connection/2240-infinite-scrolling-out-of-control
Fix infinite scrolling out of control
This commit is contained in:
commit
f6550b9b00
@ -6,11 +6,7 @@
|
|||||||
<ds-flex class="main-navigation-flex">
|
<ds-flex class="main-navigation-flex">
|
||||||
<ds-flex-item :width="{ lg: '3.5%' }" />
|
<ds-flex-item :width="{ lg: '3.5%' }" />
|
||||||
<ds-flex-item :width="{ base: '80%', sm: '80%', md: '80%', lg: '15%' }">
|
<ds-flex-item :width="{ base: '80%', sm: '80%', md: '80%', lg: '15%' }">
|
||||||
<nuxt-link
|
<nuxt-link :to="{ name: 'index' }" v-scroll-to="'.main-navigation'">
|
||||||
:to="{ name: 'index' }"
|
|
||||||
@click.native="refreshPosts({ i18n: $i18n })"
|
|
||||||
v-scroll-to="'.main-navigation'"
|
|
||||||
>
|
|
||||||
<ds-logo />
|
<ds-logo />
|
||||||
</nuxt-link>
|
</nuxt-link>
|
||||||
</ds-flex-item>
|
</ds-flex-item>
|
||||||
@ -143,7 +139,6 @@ export default {
|
|||||||
...mapActions({
|
...mapActions({
|
||||||
quickSearchClear: 'search/quickClear',
|
quickSearchClear: 'search/quickClear',
|
||||||
quickSearch: 'search/quickSearch',
|
quickSearch: 'search/quickSearch',
|
||||||
refreshPosts: 'posts/refreshPosts',
|
|
||||||
}),
|
}),
|
||||||
goToPost(item) {
|
goToPost(item) {
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
|
|||||||
@ -118,7 +118,7 @@ export default {
|
|||||||
{ src: '~/plugins/v-tooltip.js', ssr: false },
|
{ src: '~/plugins/v-tooltip.js', ssr: false },
|
||||||
{ src: '~/plugins/izi-toast.js', ssr: false },
|
{ src: '~/plugins/izi-toast.js', ssr: false },
|
||||||
{ src: '~/plugins/vue-filters.js' },
|
{ src: '~/plugins/vue-filters.js' },
|
||||||
{ src: '~/plugins/vue-infinite-scroll.js', ssr: false },
|
{ src: '~/plugins/vue-infinite-loading.js', ssr: false },
|
||||||
],
|
],
|
||||||
|
|
||||||
router: {
|
router: {
|
||||||
|
|||||||
@ -83,7 +83,7 @@
|
|||||||
"v-tooltip": "~2.0.2",
|
"v-tooltip": "~2.0.2",
|
||||||
"validator": "^12.0.0",
|
"validator": "^12.0.0",
|
||||||
"vue-count-to": "~1.0.13",
|
"vue-count-to": "~1.0.13",
|
||||||
"vue-infinite-scroll": "^2.0.2",
|
"vue-infinite-loading": "^2.4.4",
|
||||||
"vue-izitoast": "^1.2.1",
|
"vue-izitoast": "^1.2.1",
|
||||||
"vue-scrollto": "^2.17.1",
|
"vue-scrollto": "^2.17.1",
|
||||||
"vue-sweetalert-icons": "~4.2.0",
|
"vue-sweetalert-icons": "~4.2.0",
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import Styleguide from '@human-connection/styleguide'
|
|||||||
import Filters from '~/plugins/vue-filters'
|
import Filters from '~/plugins/vue-filters'
|
||||||
import VTooltip from 'v-tooltip'
|
import VTooltip from 'v-tooltip'
|
||||||
import FilterMenu from '~/components/FilterMenu/FilterMenu'
|
import FilterMenu from '~/components/FilterMenu/FilterMenu'
|
||||||
import InfiniteScroll from '~/plugins/vue-infinite-scroll'
|
import InfiniteLoading from '~/plugins/vue-infinite-loading'
|
||||||
|
|
||||||
const localVue = createLocalVue()
|
const localVue = createLocalVue()
|
||||||
|
|
||||||
@ -13,11 +13,12 @@ localVue.use(Vuex)
|
|||||||
localVue.use(Styleguide)
|
localVue.use(Styleguide)
|
||||||
localVue.use(Filters)
|
localVue.use(Filters)
|
||||||
localVue.use(VTooltip)
|
localVue.use(VTooltip)
|
||||||
localVue.use(InfiniteScroll)
|
localVue.use(InfiniteLoading)
|
||||||
|
|
||||||
config.stubs['client-only'] = '<span><slot /></span>'
|
config.stubs['client-only'] = '<span><slot /></span>'
|
||||||
config.stubs['router-link'] = '<span><slot /></span>'
|
config.stubs['router-link'] = '<span><slot /></span>'
|
||||||
config.stubs['nuxt-link'] = '<span><slot /></span>'
|
config.stubs['nuxt-link'] = '<span><slot /></span>'
|
||||||
|
config.stubs['infinite-loading'] = '<span><slot /></span>'
|
||||||
|
|
||||||
describe('PostIndex', () => {
|
describe('PostIndex', () => {
|
||||||
let wrapper
|
let wrapper
|
||||||
@ -29,7 +30,6 @@ describe('PostIndex', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
mutations = {
|
mutations = {
|
||||||
'posts/SELECT_ORDER': jest.fn(),
|
'posts/SELECT_ORDER': jest.fn(),
|
||||||
'posts/SET_CURRENT_POSTS': jest.fn(),
|
|
||||||
}
|
}
|
||||||
store = new Vuex.Store({
|
store = new Vuex.Store({
|
||||||
getters: {
|
getters: {
|
||||||
@ -54,7 +54,6 @@ describe('PostIndex', () => {
|
|||||||
'auth/user': () => {
|
'auth/user': () => {
|
||||||
return { id: 'u23' }
|
return { id: 'u23' }
|
||||||
},
|
},
|
||||||
'posts/currentPosts': () => [],
|
|
||||||
},
|
},
|
||||||
mutations,
|
mutations,
|
||||||
})
|
})
|
||||||
|
|||||||
@ -16,7 +16,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</ds-grid-item>
|
</ds-grid-item>
|
||||||
<template v-if="hasResults">
|
<template v-if="hasResults">
|
||||||
<masonry-grid-item v-for="post in currentPosts" :key="post.id">
|
<masonry-grid-item v-for="post in posts" :key="post.id">
|
||||||
<hc-post-card
|
<hc-post-card
|
||||||
:post="post"
|
:post="post"
|
||||||
:width="{ base: '100%', xs: '100%', md: '50%', xl: '33%' }"
|
:width="{ base: '100%', xs: '100%', md: '50%', xl: '33%' }"
|
||||||
@ -44,16 +44,11 @@
|
|||||||
primary
|
primary
|
||||||
/>
|
/>
|
||||||
</client-only>
|
</client-only>
|
||||||
<div
|
<client-only>
|
||||||
v-if="hasMore"
|
<infinite-loading v-if="hasMore" @infinite="showMoreContributions">
|
||||||
v-infinite-scroll="showMoreContributions"
|
|
||||||
:infinite-scroll-disabled="$apollo.loading"
|
|
||||||
:infinite-scroll-distance="10"
|
|
||||||
:infinite-scroll-throttle-delay="800"
|
|
||||||
:infinite-scroll-immediate-check="true"
|
|
||||||
>
|
|
||||||
<hc-load-more :loading="$apollo.loading" @click="showMoreContributions" />
|
<hc-load-more :loading="$apollo.loading" @click="showMoreContributions" />
|
||||||
</div>
|
</infinite-loading>
|
||||||
|
</client-only>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -97,7 +92,6 @@ export default {
|
|||||||
orderBy: 'posts/orderBy',
|
orderBy: 'posts/orderBy',
|
||||||
selectedOrder: 'posts/selectedOrder',
|
selectedOrder: 'posts/selectedOrder',
|
||||||
sortingIcon: 'posts/orderIcon',
|
sortingIcon: 'posts/orderIcon',
|
||||||
currentPosts: 'posts/currentPosts',
|
|
||||||
}),
|
}),
|
||||||
selected: {
|
selected: {
|
||||||
get() {
|
get() {
|
||||||
@ -105,7 +99,7 @@ export default {
|
|||||||
},
|
},
|
||||||
set({ value }) {
|
set({ value }) {
|
||||||
this.offset = 0
|
this.offset = 0
|
||||||
this.setCurrentPosts([])
|
this.posts = []
|
||||||
this.selectOrder(value)
|
this.selectOrder(value)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -123,13 +117,12 @@ export default {
|
|||||||
return filter
|
return filter
|
||||||
},
|
},
|
||||||
hasResults() {
|
hasResults() {
|
||||||
return this.$apollo.loading || (this.currentPosts && this.currentPosts.length > 0)
|
return this.$apollo.loading || (this.posts && this.posts.length > 0)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapMutations({
|
...mapMutations({
|
||||||
selectOrder: 'posts/SELECT_ORDER',
|
selectOrder: 'posts/SELECT_ORDER',
|
||||||
setCurrentPosts: 'posts/SET_CURRENT_POSTS',
|
|
||||||
}),
|
}),
|
||||||
clearSearch() {
|
clearSearch() {
|
||||||
this.$router.push({ path: '/' })
|
this.$router.push({ path: '/' })
|
||||||
@ -141,7 +134,7 @@ export default {
|
|||||||
params: { id: post.id, slug: post.slug },
|
params: { id: post.id, slug: post.slug },
|
||||||
}).href
|
}).href
|
||||||
},
|
},
|
||||||
showMoreContributions() {
|
showMoreContributions($state) {
|
||||||
const { Post: PostQuery } = this.$apollo.queries
|
const { Post: PostQuery } = this.$apollo.queries
|
||||||
if (!PostQuery) return // seems this can be undefined on subpages
|
if (!PostQuery) return // seems this can be undefined on subpages
|
||||||
|
|
||||||
@ -156,7 +149,9 @@ export default {
|
|||||||
updateQuery: (previousResult, { fetchMoreResult }) => {
|
updateQuery: (previousResult, { fetchMoreResult }) => {
|
||||||
if (!fetchMoreResult || fetchMoreResult.Post.length < this.pageSize) {
|
if (!fetchMoreResult || fetchMoreResult.Post.length < this.pageSize) {
|
||||||
this.hasMore = false
|
this.hasMore = false
|
||||||
|
$state.complete()
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = {
|
const result = {
|
||||||
...previousResult,
|
...previousResult,
|
||||||
Post: [
|
Post: [
|
||||||
@ -168,19 +163,19 @@ export default {
|
|||||||
...fetchMoreResult.Post,
|
...fetchMoreResult.Post,
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
this.setCurrentPosts(result.Post)
|
$state.loaded()
|
||||||
|
return result
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
deletePost(deletedPost) {
|
deletePost(deletedPost) {
|
||||||
const posts = this.currentPosts.filter(post => {
|
this.posts = this.posts.filter(post => {
|
||||||
return post.id !== deletedPost.id
|
return post.id !== deletedPost.id
|
||||||
})
|
})
|
||||||
this.setCurrentPosts(posts)
|
|
||||||
},
|
},
|
||||||
resetPostList() {
|
resetPostList() {
|
||||||
this.offset = 0
|
this.offset = 0
|
||||||
this.setCurrentPosts([])
|
this.posts = []
|
||||||
this.hasMore = true
|
this.hasMore = true
|
||||||
},
|
},
|
||||||
pinPost(post) {
|
pinPost(post) {
|
||||||
@ -224,7 +219,7 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
update({ Post }) {
|
update({ Post }) {
|
||||||
this.setCurrentPosts(Post)
|
this.posts = Post
|
||||||
},
|
},
|
||||||
fetchPolicy: 'cache-and-network',
|
fetchPolicy: 'cache-and-network',
|
||||||
},
|
},
|
||||||
|
|||||||
@ -3,19 +3,20 @@ import ProfileSlug from './_slug.vue'
|
|||||||
import Vuex from 'vuex'
|
import Vuex from 'vuex'
|
||||||
import Styleguide from '@human-connection/styleguide'
|
import Styleguide from '@human-connection/styleguide'
|
||||||
import Filters from '~/plugins/vue-filters'
|
import Filters from '~/plugins/vue-filters'
|
||||||
import InfiniteScroll from '~/plugins/vue-infinite-scroll'
|
import InfiniteLoading from '~/plugins/vue-infinite-loading'
|
||||||
|
|
||||||
const localVue = createLocalVue()
|
const localVue = createLocalVue()
|
||||||
|
|
||||||
localVue.use(Vuex)
|
localVue.use(Vuex)
|
||||||
localVue.use(Styleguide)
|
localVue.use(Styleguide)
|
||||||
localVue.use(Filters)
|
localVue.use(Filters)
|
||||||
localVue.use(InfiniteScroll)
|
localVue.use(InfiniteLoading)
|
||||||
localVue.filter('date', d => d)
|
localVue.filter('date', d => d)
|
||||||
|
|
||||||
config.stubs['client-only'] = '<span><slot /></span>'
|
config.stubs['client-only'] = '<span><slot /></span>'
|
||||||
config.stubs['v-popover'] = '<span><slot /></span>'
|
config.stubs['v-popover'] = '<span><slot /></span>'
|
||||||
config.stubs['nuxt-link'] = '<span><slot /></span>'
|
config.stubs['nuxt-link'] = '<span><slot /></span>'
|
||||||
|
config.stubs['infinite-loading'] = '<span><slot /></span>'
|
||||||
|
|
||||||
describe('ProfileSlug', () => {
|
describe('ProfileSlug', () => {
|
||||||
let wrapper
|
let wrapper
|
||||||
@ -127,23 +128,6 @@ describe('ProfileSlug', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('pagination returned less posts than available', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
const posts = [1, 2, 3, 4, 5].map(id => {
|
|
||||||
return {
|
|
||||||
...aPost,
|
|
||||||
id,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
wrapper.setData({ posts, hasMore: true })
|
|
||||||
})
|
|
||||||
|
|
||||||
it('does not display a "load more" button', () => {
|
|
||||||
expect(wrapper.find('.load-more').exists()).toBe(false)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('pagination returned at least as many posts as pageSize', () => {
|
describe('pagination returned at least as many posts as pageSize', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
const posts = [1, 2, 3, 4, 5, 6].map(id => {
|
const posts = [1, 2, 3, 4, 5, 6].map(id => {
|
||||||
|
|||||||
@ -255,16 +255,11 @@
|
|||||||
</ds-grid-item>
|
</ds-grid-item>
|
||||||
</template>
|
</template>
|
||||||
</masonry-grid>
|
</masonry-grid>
|
||||||
<div
|
<client-only>
|
||||||
v-if="hasMore && posts.length >= pageSize"
|
<infinite-loading v-if="hasMore" @infinite="showMoreContributions">
|
||||||
v-infinite-scroll="showMoreContributions"
|
|
||||||
:infinite-scroll-disabled="$apollo.loading"
|
|
||||||
:infinite-scroll-distance="10"
|
|
||||||
:infinite-scroll-throttle-delay="800"
|
|
||||||
:infinite-scroll-immediate-check="true"
|
|
||||||
>
|
|
||||||
<hc-load-more :loading="$apollo.loading" @click="showMoreContributions" />
|
<hc-load-more :loading="$apollo.loading" @click="showMoreContributions" />
|
||||||
</div>
|
</infinite-loading>
|
||||||
|
</client-only>
|
||||||
</ds-flex-item>
|
</ds-flex-item>
|
||||||
</ds-flex>
|
</ds-flex>
|
||||||
</div>
|
</div>
|
||||||
@ -378,7 +373,7 @@ export default {
|
|||||||
uniq(items, field = 'id') {
|
uniq(items, field = 'id') {
|
||||||
return uniqBy(items, field)
|
return uniqBy(items, field)
|
||||||
},
|
},
|
||||||
showMoreContributions() {
|
showMoreContributions($state) {
|
||||||
const { profilePagePosts: PostQuery } = this.$apollo.queries
|
const { profilePagePosts: PostQuery } = this.$apollo.queries
|
||||||
if (!PostQuery) return // seems this can be undefined on subpages
|
if (!PostQuery) return // seems this can be undefined on subpages
|
||||||
this.offset += this.pageSize
|
this.offset += this.pageSize
|
||||||
@ -393,6 +388,7 @@ export default {
|
|||||||
updateQuery: (previousResult, { fetchMoreResult }) => {
|
updateQuery: (previousResult, { fetchMoreResult }) => {
|
||||||
if (!fetchMoreResult || fetchMoreResult.profilePagePosts.length < this.pageSize) {
|
if (!fetchMoreResult || fetchMoreResult.profilePagePosts.length < this.pageSize) {
|
||||||
this.hasMore = false
|
this.hasMore = false
|
||||||
|
$state.complete()
|
||||||
}
|
}
|
||||||
const result = {
|
const result = {
|
||||||
...previousResult,
|
...previousResult,
|
||||||
@ -406,6 +402,7 @@ export default {
|
|||||||
...fetchMoreResult.profilePagePosts,
|
...fetchMoreResult.profilePagePosts,
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
$state.loaded()
|
||||||
return result
|
return result
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
4
webapp/plugins/vue-infinite-loading.js
Normal file
4
webapp/plugins/vue-infinite-loading.js
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import Vue from 'vue'
|
||||||
|
import InfiniteLoading from 'vue-infinite-loading'
|
||||||
|
|
||||||
|
Vue.use(InfiniteLoading, { props: { distance: 10 }, system: { throttleLimit: 800 } })
|
||||||
@ -1,4 +0,0 @@
|
|||||||
import Vue from 'vue'
|
|
||||||
import infiniteScroll from 'vue-infinite-scroll'
|
|
||||||
|
|
||||||
Vue.use(infiniteScroll)
|
|
||||||
@ -4,8 +4,6 @@ import xor from 'lodash/xor'
|
|||||||
import isEmpty from 'lodash/isEmpty'
|
import isEmpty from 'lodash/isEmpty'
|
||||||
import isEqual from 'lodash/isEqual'
|
import isEqual from 'lodash/isEqual'
|
||||||
import clone from 'lodash/clone'
|
import clone from 'lodash/clone'
|
||||||
import { filterPosts } from '~/graphql/PostQuery'
|
|
||||||
import { first, offset } from '~/constants/posts'
|
|
||||||
|
|
||||||
const defaultFilter = {}
|
const defaultFilter = {}
|
||||||
|
|
||||||
@ -28,7 +26,6 @@ export const state = () => {
|
|||||||
...defaultFilter,
|
...defaultFilter,
|
||||||
},
|
},
|
||||||
order: orderOptions['createdAt_desc'],
|
order: orderOptions['createdAt_desc'],
|
||||||
currentPosts: [],
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,9 +74,6 @@ export const mutations = {
|
|||||||
SELECT_ORDER(state, value) {
|
SELECT_ORDER(state, value) {
|
||||||
state.order = orderOptions[value]
|
state.order = orderOptions[value]
|
||||||
},
|
},
|
||||||
SET_CURRENT_POSTS(state, posts) {
|
|
||||||
state.currentPosts = posts
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getters = {
|
export const getters = {
|
||||||
@ -120,26 +114,4 @@ export const getters = {
|
|||||||
orderIcon(state) {
|
orderIcon(state) {
|
||||||
return state.order.icon
|
return state.order.icon
|
||||||
},
|
},
|
||||||
currentPosts(state) {
|
|
||||||
return state.currentPosts || []
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
export const actions = {
|
|
||||||
async refreshPosts({ commit, getters }, { i18n }) {
|
|
||||||
const client = this.app.apolloProvider.defaultClient
|
|
||||||
const {
|
|
||||||
data: { Post },
|
|
||||||
} = await client.query({
|
|
||||||
query: filterPosts(i18n),
|
|
||||||
variables: {
|
|
||||||
filter: getters.filter,
|
|
||||||
first,
|
|
||||||
orderBy: ['pinned_asc', getters.orderBy],
|
|
||||||
offset,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
commit('SET_CURRENT_POSTS', Post)
|
|
||||||
return Post
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16331,10 +16331,10 @@ vue-hot-reload-api@^2.3.0:
|
|||||||
resolved "https://registry.yarnpkg.com/vue-hot-reload-api/-/vue-hot-reload-api-2.3.3.tgz#2756f46cb3258054c5f4723de8ae7e87302a1ccf"
|
resolved "https://registry.yarnpkg.com/vue-hot-reload-api/-/vue-hot-reload-api-2.3.3.tgz#2756f46cb3258054c5f4723de8ae7e87302a1ccf"
|
||||||
integrity sha512-KmvZVtmM26BQOMK1rwUZsrqxEGeKiYSZGA7SNWE6uExx8UX/cj9hq2MRV/wWC3Cq6AoeDGk57rL9YMFRel/q+g==
|
integrity sha512-KmvZVtmM26BQOMK1rwUZsrqxEGeKiYSZGA7SNWE6uExx8UX/cj9hq2MRV/wWC3Cq6AoeDGk57rL9YMFRel/q+g==
|
||||||
|
|
||||||
vue-infinite-scroll@^2.0.2:
|
vue-infinite-loading@^2.4.4:
|
||||||
version "2.0.2"
|
version "2.4.4"
|
||||||
resolved "https://registry.yarnpkg.com/vue-infinite-scroll/-/vue-infinite-scroll-2.0.2.tgz#ca37a91fe92ee0ad3b74acf8682c00917144b711"
|
resolved "https://registry.yarnpkg.com/vue-infinite-loading/-/vue-infinite-loading-2.4.4.tgz#8a9defb9ceeea797c057cb36bdf558a4b2ce409f"
|
||||||
integrity sha512-n+YghR059YmciANGJh9SsNWRi1YZEBVlODtmnb/12zI+4R72QZSWd+EuZ5mW6auEo/yaJXgxzwsuhvALVnm73A==
|
integrity sha512-eIFBcyKqkivtsDDq7Ee5ybDJVGLxIzU1NcBJCHG7Zx9Ic66QEGzSPs2OPJlGUdtu0/RS7KpUER35ZP/a7FdSOg==
|
||||||
|
|
||||||
vue-izitoast@^1.2.1:
|
vue-izitoast@^1.2.1:
|
||||||
version "1.2.1"
|
version "1.2.1"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user