Skip to content

Commit cf97063

Browse files
authored
feat: APPS-3111 Add infinite scrolling for mobile screens for series listing page (#89)
* add scrolling * add finally * linting * remove console logs * infinitescroll past is returning current when refreshed * linting * remove extra line * fix linting error 184:3 error Return statement should not contain assignment * linting * linting fix * past and current working in mobile view * fix infinite scroll
1 parent f5f488a commit cf97063

File tree

3 files changed

+184
-20
lines changed

3 files changed

+184
-20
lines changed

gql/queries/FTVAEventSeriesDetail.gql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ query FTVAEventSeriesDetail ($slug: [String!]) {
1313
id
1414
typeHandle
1515
sectionHandle
16+
uri
1617
title
1718
guestSpeaker
1819
ftvaEventIntroduction

pages/events/index.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ async function searchES() {
243243
results = await paginatedSearchFilters('ftvaEvent', userFilterSelection.value, userDateSelection.value, 'startDate', 'asc')
244244
}
245245
246-
if (results && results.hits && results.hits.total.value > 0) {
246+
if (results && results.hits && results.hits.hits.length > 0) {
247247
const newEvents = results.hits.hits || []
248248
if (isMobile.value) {
249249
mobileEvents.value.push(...newEvents)

pages/series/index.vue

Lines changed: 182 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@ import { DividerWayFinder, SectionStaffArticleList, SectionPagination, TabItem,
44
55
// HELPERS
66
import _get from 'lodash/get'
7-
import FTVAEventSeriesList from '../gql/queries/FTVAEventSeriesList.gql'
7+
import { useWindowSize, useInfiniteScroll } from '@vueuse/core'
88
99
// GQL - start
10+
import FTVAEventSeriesList from '../gql/queries/FTVAEventSeriesList.gql'
11+
1012
const { $graphql } = useNuxtApp()
1113
1214
const { data, error } = await useAsyncData('series-list', async () => {
@@ -19,7 +21,6 @@ if (error.value) {
1921
})
2022
}
2123
if (!data.value.entries) {
22-
// console.log('no data')
2324
throw createError({
2425
statusCode: 404,
2526
statusMessage: 'Page Not Found',
@@ -30,17 +31,81 @@ if (!data.value.entries) {
3031
const heading = ref(_get(data.value, 'entry', {}))
3132
// GQL - End
3233
33-
// STATE VARIABLES - ARGUMENTS on useEventSeriesListSearchFilter
34-
const series = ref([]) // Add typescript
35-
const currentView = ref('current') // Tracks 'current' or 'past'
34+
// STATE MANAGEMENT
35+
const desktopPage = useState('desktopPage', () => 1) // Persist desktop page
36+
const desktopSeries = ref([]) // Desktop series list
37+
const mobileSeries = ref([]) // Mobile series list
38+
const series = computed(() => (isMobile.value ? mobileSeries.value : desktopSeries.value))
39+
const currentView = computed(() => route.query.view || 'current') // Tracks 'current' or 'past'
3640
const noResultsFound = ref(false)
3741
const documentsPerPage = 10
3842
const totalPages = ref(0)
3943
const currentPage = ref(1)
4044
const route = useRoute()
4145
42-
// Helper functions copied from event index
43-
// Get data for Image
46+
// INFINITE SCROLLING
47+
const isLoading = ref(false)
48+
const isMobile = ref(false)
49+
const hasMore = ref(true) // Flag to control infinite scroll
50+
const scrollElPast = ref(null)
51+
const scrollElCurrent = ref(null)
52+
// const desktopEl = ref(null)
53+
const pastScroll = useInfiniteScroll(
54+
scrollElPast,
55+
async () => {
56+
if (isMobile.value && hasMore.value && !isLoading.value) {
57+
currentPage.value++
58+
await searchES()
59+
}
60+
},
61+
{ distance: 100 }
62+
)
63+
const currentScroll = useInfiniteScroll(
64+
scrollElCurrent,
65+
async () => {
66+
if (isMobile.value && hasMore.value && !isLoading.value) {
67+
currentPage.value++
68+
await searchES()
69+
}
70+
},
71+
{ distance: 100 }
72+
)
73+
74+
// WINDOW SIZE HANDLING
75+
const { width } = useWindowSize()
76+
watch(width, (newWidth) => {
77+
const wasMobile = isMobile.value
78+
isMobile.value = newWidth <= 750
79+
80+
// Reinitialize only when transitioning between mobile and desktop
81+
if (wasMobile !== isMobile.value) {
82+
handleScreenTransition()
83+
}
84+
}, { immediate: true })
85+
86+
// HANDLE SCREEN TRANSITIONS
87+
function handleScreenTransition() {
88+
if (isMobile.value) {
89+
// Switching to mobile: save desktop page, clear query param
90+
91+
desktopPage.value = currentPage.value
92+
currentPage.value = 1
93+
mobileSeries.value = []
94+
hasMore.value = true
95+
const { page, ...remainingQuery } = route.query
96+
useRouter().push({ query: { ...remainingQuery, view: currentView.value } })
97+
} else {
98+
// Switching to desktop: restore query param
99+
if (totalPages.value === 1)
100+
desktopPage.value = 1
101+
const restoredPage = desktopPage.value || 1
102+
useRouter().push({ query: { ...route.query, page: restoredPage.toString(), view: currentView.value } })
103+
currentPage.value = restoredPage
104+
desktopSeries.value = []
105+
}
106+
}
107+
108+
// GET DATA FOR IMAGE
44109
function parsedImage(obj) {
45110
return obj._source.imageCarousel
46111
}
@@ -51,7 +116,6 @@ function isImageExists(obj) {
51116
52117
// FORMATTED COMPUTED EVENTS
53118
const parsedEventSeries = computed(() => {
54-
console.log(series.value)
55119
if (series.value.length === 0) return []
56120
57121
return series.value.map((obj) => {
@@ -69,36 +133,48 @@ const parsedEventSeries = computed(() => {
69133
70134
// ES FUNCTION
71135
async function searchES() {
136+
if (isLoading.value || !hasMore.value) return
137+
138+
isLoading.value = true
72139
// COMPOSABLE
73140
const { currentEventSeriesQuery, pastEventSeriesQuery } = useEventSeriesListSearchFilter()
74141
75142
try {
76143
let results
144+
77145
if (currentView.value === 'current') {
78146
results = await currentEventSeriesQuery(currentPage.value,
79-
documentsPerPage.value,
147+
documentsPerPage,
80148
'startDate',
81149
'asc',
82150
['*'],)
83151
} else {
84152
results = await pastEventSeriesQuery(currentPage.value,
85-
documentsPerPage.value,
153+
documentsPerPage,
86154
'startDate',
87155
'desc',
88156
['*'])
89157
}
90158
91-
if (results?.hits?.total?.value > 0) {
92-
series.value = results.hits.hits
159+
if (results?.hits?.hits?.length > 0) {
160+
const newSeries = results.hits.hits || []
161+
if (isMobile.value) {
162+
mobileSeries.value.push(...newSeries)
163+
hasMore.value = currentPage.value < Math.ceil(results.hits.total.value / documentsPerPage)
164+
} else {
165+
desktopSeries.value = newSeries
166+
totalPages.value = Math.ceil(results.hits.total.value / documentsPerPage)
167+
}
93168
noResultsFound.value = false
94-
totalPages.value = Math.ceil(results.hits.total.value / documentsPerPage)
95169
} else {
96-
series.value = []
97170
noResultsFound.value = true
171+
if (!isMobile.value) totalPages.value = 0
172+
hasMore.value = false
98173
}
99174
} catch (err) {
100-
console.error('Error fetching series:', err)
101175
noResultsFound.value = true
176+
} finally {
177+
isLoading.value = false
102178
}
103179
}
104180
@@ -109,9 +185,11 @@ const parseViewSelection = computed(() => {
109185
watch(
110186
() => route.query,
111187
(newVal, oldVal) => {
112-
currentView.value = route.query.view || 'current'
113-
searchES()
188+
isLoading.value = false
114189
currentPage.value = route.query.page ? parseInt(route.query.page) : 1
190+
isMobile.value ? mobileSeries.value = [] : desktopSeries.value = []
191+
hasMore.value = true
192+
searchES()
115193
}, { deep: true, immediate: true }
116194
)
117195
</script>
@@ -128,6 +206,7 @@ watch(
128206
129207
<SectionWrapper theme="paleblue">
130208
<TabList
209+
v-if="!isMobile"
131210
alignment="center"
132211
:initial-tab="parseViewSelection"
133212
>
@@ -139,7 +218,10 @@ watch(
139218
<SectionStaffArticleList :items="parsedEventSeries" />
140219
141220
<SectionPagination
142-
v-if="totalPages !== 1"
221+
v-if="
222+
totalPages
223+
!== 1"
224+
class="pagination"
143225
:pages="totalPages"
144226
:initial-current-page="currentPage"
145227
/>
@@ -191,12 +273,87 @@ watch(
191273
</template>
192274
</TabItem>
193275
</TabList>
276+
277+
<TabList
278+
v-else
279+
alignment="center"
280+
:initial-tab="parseViewSelection"
281+
>
282+
<TabItem
283+
title="Past Series"
284+
class="tab-content"
285+
>
286+
<template v-if="parsedEventSeries && parsedEventSeries.length > 0">
287+
<SectionStaffArticleList
288+
ref="scrollElPast"
289+
:items="parsedEventSeries"
290+
/>
291+
292+
<SectionPagination
293+
v-if="
294+
totalPages
295+
!== 1"
296+
class="pagination"
297+
:pages="totalPages"
298+
:initial-current-page="currentPage"
299+
/>
300+
</template>
301+
302+
<template v-else>
303+
<p
304+
v-if="noResultsFound"
305+
class="empty-tab"
306+
>
307+
There are no past event series
308+
</p>
309+
<p
310+
v-else
311+
class="empty-tab"
312+
>
313+
Data loading in progress ...
314+
</p>
315+
</template>
316+
</TabItem>
317+
318+
<TabItem
319+
title="Current and Upcoming Series"
320+
class="tab-content"
321+
>
322+
<template v-if="parsedEventSeries && parsedEventSeries.length > 0">
323+
<SectionStaffArticleList
324+
ref="scrollElCurrent"
325+
:items="parsedEventSeries"
326+
/>
327+
328+
<SectionPagination
329+
v-if="totalPages !== 1"
330+
:pages="totalPages"
331+
:initial-current-page="currentPage"
332+
/>
333+
</template>
334+
335+
<template v-else>
336+
<p
337+
v-if="noResultsFound"
338+
class="empty-tab"
339+
>
340+
There are no current or upcoming event series
341+
</p>
342+
<p
343+
v-else
344+
class="empty-tab"
345+
>
346+
Data loading in progress ...
347+
</p>
348+
</template>
349+
</TabItem>
350+
</TabList>
194351
</SectionWrapper>
195352
</div>
196353
</div>
197354
</template>
198355
199-
<style scoped>
356+
<style lang="scss" scoped>
200357
@import 'assets/styles/listing-pages.scss';
201358
202359
.page-event-series {
@@ -233,5 +390,11 @@ watch(
233390
max-width: unset;
234391
padding: 2.5%;
235392
}
393+
394+
@media #{$small} {
395+
.pagination {
396+
display: none;
397+
}
398+
}
236399
}
237400
</style>

0 commit comments

Comments
 (0)