Add pagination for albums + add loading indicators
This commit is contained in:
parent
a9edb148f1
commit
1dbf4e4821
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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']),
|
||||
|
|
|
@ -5,7 +5,9 @@ import PhotoGallery from '../photoGallery/PhotoGallery'
|
|||
import AlbumBoxes from './AlbumBoxes'
|
||||
import AlbumFilter from '../AlbumFilter'
|
||||
|
||||
const AlbumGallery = ({
|
||||
const AlbumGallery = React.forwardRef(
|
||||
(
|
||||
{
|
||||
album,
|
||||
loading = false,
|
||||
customAlbumLink,
|
||||
|
@ -15,7 +17,9 @@ const AlbumGallery = ({
|
|||
ordering,
|
||||
onlyFavorites = false,
|
||||
onFavorite,
|
||||
}) => {
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
const [imageState, setImageState] = useState({
|
||||
activeImage: -1,
|
||||
presenting: false,
|
||||
|
@ -86,7 +90,7 @@ const AlbumGallery = ({
|
|||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div ref={ref}>
|
||||
<AlbumTitle album={album} disableLink />
|
||||
{showFilter && (
|
||||
<AlbumFilter
|
||||
|
@ -120,9 +124,10 @@ const AlbumGallery = ({
|
|||
nextImage={nextImage}
|
||||
previousImage={previousImage}
|
||||
/>
|
||||
</>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
AlbumGallery.propTypes = {
|
||||
album: PropTypes.object,
|
||||
|
|
|
@ -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={
|
||||
|
|
|
@ -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,19 +15,33 @@ 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 => {
|
||||
observerElem.current = node
|
||||
|
@ -55,10 +70,11 @@ const useScrollPagination = ({ loading, fetchMore }) => {
|
|||
// reconfigure observer if fetchMore function changes
|
||||
useEffect(() => {
|
||||
reconfigureIntersectionObserver()
|
||||
}, [fetchMore])
|
||||
}, [fetchMore, data, finished])
|
||||
|
||||
return {
|
||||
containerElem,
|
||||
finished,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue