1
Fork 0

Add pagination for albums + add loading indicators

This commit is contained in:
viktorstrate 2021-02-12 23:57:15 +01:00
parent a9edb148f1
commit 1dbf4e4821
No known key found for this signature in database
GPG Key ID: 3F855605109C1E8A
5 changed files with 192 additions and 132 deletions

View File

@ -5,6 +5,8 @@ import AlbumGallery from '../../components/albumGallery/AlbumGallery'
import PropTypes from 'prop-types'
import Layout from '../../Layout'
import useURLParameters from '../../hooks/useURLParameters'
import useScrollPagination from '../../hooks/useScrollPagination'
import { Loader } from 'semantic-ui-react'
const albumQuery = gql`
query albumQuery(
@ -12,6 +14,8 @@ const albumQuery = gql`
$onlyFavorites: Boolean
$mediaOrderBy: String
$mediaOrderDirection: OrderDirection
$limit: Int
$offset: Int
) {
album(id: $id) {
id
@ -27,6 +31,8 @@ const albumQuery = gql`
}
media(
filter: {
limit: $limit
offset: $offset
order_by: $mediaOrderBy
order_direction: $mediaOrderDirection
}
@ -80,15 +86,24 @@ function AlbumPage({ match }) {
[setParams]
)
const { loading, error, data, refetch } = useQuery(albumQuery, {
const { loading, error, data, refetch, fetchMore } = useQuery(albumQuery, {
variables: {
id: albumId,
onlyFavorites,
mediaOrderBy: orderBy,
mediaOrderDirection: orderDirection,
offset: 0,
limit: 200,
},
})
const { containerElem, finished: finishedLoadingMore } = useScrollPagination({
loading,
fetchMore,
data,
getItems: data => data.album.media,
})
const toggleFavorites = useCallback(
onlyFavorites => {
if (
@ -115,6 +130,7 @@ function AlbumPage({ match }) {
return (
<Layout title={data ? data.album.title : 'Loading album'}>
<AlbumGallery
ref={containerElem}
album={data && data.album}
loading={loading}
showFavoritesToggle
@ -125,6 +141,13 @@ function AlbumPage({ match }) {
setOrdering={setOrdering}
ordering={{ orderBy, orderDirection }}
/>
<Loader
style={{ margin: '42px 0 24px 0' }}
active={!finishedLoadingMore && !loading}
inline="centered"
>
Loading more media
</Loader>
</Layout>
)
}

View File

@ -114,6 +114,20 @@ const memoryCache = new InMemoryCache({
SiteInfo: {
merge: true,
},
MediaURL: {
keyFields: ['url'],
},
Album: {
fields: {
media: {
keyArgs: ['onlyFavorites'],
merge(existing = [], incoming) {
console.log('merge media', existing, incoming)
return [...existing, ...incoming]
},
},
},
},
Query: {
fields: {
myTimeline: offsetLimitPagination(['onlyFavorites']),

View File

@ -5,124 +5,129 @@ import PhotoGallery from '../photoGallery/PhotoGallery'
import AlbumBoxes from './AlbumBoxes'
import AlbumFilter from '../AlbumFilter'
const AlbumGallery = ({
album,
loading = false,
customAlbumLink,
showFilter = false,
setOnlyFavorites,
setOrdering,
ordering,
onlyFavorites = false,
onFavorite,
}) => {
const [imageState, setImageState] = useState({
activeImage: -1,
presenting: false,
})
const AlbumGallery = React.forwardRef(
(
{
album,
loading = false,
customAlbumLink,
showFilter = false,
setOnlyFavorites,
setOrdering,
ordering,
onlyFavorites = false,
onFavorite,
},
ref
) => {
const [imageState, setImageState] = useState({
activeImage: -1,
presenting: false,
})
const setPresenting = presenting =>
setImageState(state => ({ ...state, presenting }))
const setPresenting = presenting =>
setImageState(state => ({ ...state, presenting }))
const setPresentingWithHistory = presenting => {
setPresenting(presenting)
if (presenting) {
history.pushState({ imageState }, '')
} else {
history.back()
}
}
const updateHistory = imageState => {
history.replaceState({ imageState }, '')
return imageState
}
const setActiveImage = activeImage => {
setImageState(state => updateHistory({ ...state, activeImage }))
}
const nextImage = () => {
setActiveImage((imageState.activeImage + 1) % album.media.length)
}
const previousImage = () => {
if (imageState.activeImage <= 0) {
setActiveImage(album.media.length - 1)
} else {
setActiveImage(imageState.activeImage - 1)
}
}
useEffect(() => {
const updateImageState = event => {
setImageState(event.state.imageState)
}
window.addEventListener('popstate', updateImageState)
return () => {
window.removeEventListener('popstate', updateImageState)
}
}, [imageState])
useEffect(() => {
setActiveImage(-1)
}, [album])
let subAlbumElement = null
if (album) {
if (album.subAlbums.length > 0) {
subAlbumElement = (
<AlbumBoxes
loading={loading}
albums={album.subAlbums}
getCustomLink={customAlbumLink}
/>
)
}
} else {
subAlbumElement = <AlbumBoxes loading={loading} />
}
return (
<>
<AlbumTitle album={album} disableLink />
{showFilter && (
<AlbumFilter
onlyFavorites={onlyFavorites}
setOnlyFavorites={setOnlyFavorites}
setOrdering={setOrdering}
ordering={ordering}
/>
)}
{subAlbumElement}
{
<h2
style={{
opacity: loading ? 0 : 1,
display: album && album.subAlbums.length > 0 ? 'block' : 'none',
}}
>
Images
</h2>
const setPresentingWithHistory = presenting => {
setPresenting(presenting)
if (presenting) {
history.pushState({ imageState }, '')
} else {
history.back()
}
<PhotoGallery
loading={loading}
media={album && album.media}
activeIndex={imageState.activeImage}
presenting={imageState.presenting}
onSelectImage={index => {
setActiveImage(index)
}}
onFavorite={onFavorite}
setPresenting={setPresentingWithHistory}
nextImage={nextImage}
previousImage={previousImage}
/>
</>
)
}
}
const updateHistory = imageState => {
history.replaceState({ imageState }, '')
return imageState
}
const setActiveImage = activeImage => {
setImageState(state => updateHistory({ ...state, activeImage }))
}
const nextImage = () => {
setActiveImage((imageState.activeImage + 1) % album.media.length)
}
const previousImage = () => {
if (imageState.activeImage <= 0) {
setActiveImage(album.media.length - 1)
} else {
setActiveImage(imageState.activeImage - 1)
}
}
useEffect(() => {
const updateImageState = event => {
setImageState(event.state.imageState)
}
window.addEventListener('popstate', updateImageState)
return () => {
window.removeEventListener('popstate', updateImageState)
}
}, [imageState])
useEffect(() => {
setActiveImage(-1)
}, [album])
let subAlbumElement = null
if (album) {
if (album.subAlbums.length > 0) {
subAlbumElement = (
<AlbumBoxes
loading={loading}
albums={album.subAlbums}
getCustomLink={customAlbumLink}
/>
)
}
} else {
subAlbumElement = <AlbumBoxes loading={loading} />
}
return (
<div ref={ref}>
<AlbumTitle album={album} disableLink />
{showFilter && (
<AlbumFilter
onlyFavorites={onlyFavorites}
setOnlyFavorites={setOnlyFavorites}
setOrdering={setOrdering}
ordering={ordering}
/>
)}
{subAlbumElement}
{
<h2
style={{
opacity: loading ? 0 : 1,
display: album && album.subAlbums.length > 0 ? 'block' : 'none',
}}
>
Images
</h2>
}
<PhotoGallery
loading={loading}
media={album && album.media}
activeIndex={imageState.activeImage}
presenting={imageState.presenting}
onSelectImage={index => {
setActiveImage(index)
}}
onFavorite={onFavorite}
setPresenting={setPresentingWithHistory}
nextImage={nextImage}
previousImage={previousImage}
/>
</div>
)
}
)
AlbumGallery.propTypes = {
album: PropTypes.object,

View File

@ -130,21 +130,16 @@ const TimelineGallery = () => {
variables: {
onlyFavorites,
offset: 0,
limit: 10,
limit: 50,
},
}
)
const { containerElem } = useScrollPagination({
const { containerElem, finished: finishedLoadingMore } = useScrollPagination({
loading,
fetchMore: () => {
console.log('fetching more')
fetchMore({
variables: {
offset: data.myTimeline.length,
},
})
},
fetchMore,
data,
getItems: data => data.myTimeline,
})
useEffect(() => {
@ -209,6 +204,13 @@ const TimelineGallery = () => {
setOnlyFavorites={setOnlyFavorites}
/>
<GalleryWrapper ref={containerElem}>{timelineGroups}</GalleryWrapper>
<Loader
style={{ margin: '42px 0 24px 0' }}
active={!finishedLoadingMore && !loading}
inline="centered"
>
Loading more media
</Loader>
{presenting && (
<PresentView
media={

View File

@ -1,8 +1,9 @@
import { useCallback, useEffect, useRef } from 'react'
import { useCallback, useEffect, useRef, useState } from 'react'
const useScrollPagination = ({ loading, fetchMore }) => {
const useScrollPagination = ({ loading, fetchMore, data, getItems }) => {
const observer = useRef(null)
const observerElem = useRef(null)
const [finished, setFinished] = useState(false)
const reconfigureIntersectionObserver = () => {
var options = {
@ -14,18 +15,32 @@ const useScrollPagination = ({ loading, fetchMore }) => {
// delete old observer
if (observer.current) observer.current.disconnect()
if (finished) return
// configure new observer
observer.current = new IntersectionObserver(entities => {
console.log('Observing', entities)
if (entities.find(x => x.isIntersecting == false)) {
console.log('load more')
fetchMore()
let itemCount = getItems(data).length
console.log('load more', itemCount)
fetchMore({
variables: {
offset: itemCount,
},
}).then(result => {
const newItemCount = getItems(result.data).length
console.log('then', result, itemCount, newItemCount)
if (newItemCount == 0) {
setFinished(true)
}
})
}
}, options)
// activate new observer
if (observerElem.current && !loading)
if (observerElem.current && !loading) {
observer.current.observe(observerElem.current)
}
}
const containerElem = useCallback(node => {
@ -55,10 +70,11 @@ const useScrollPagination = ({ loading, fetchMore }) => {
// reconfigure observer if fetchMore function changes
useEffect(() => {
reconfigureIntersectionObserver()
}, [fetchMore])
}, [fetchMore, data, finished])
return {
containerElem,
finished,
}
}