mirror of
https://git.vectorsigma.ru/public/photoview.git
synced 2026-08-03 23:29:11 +00:00
Merge pull request #193 from photoview/scroll-pagination
Infinite scroll pagination
This commit is contained in:
@@ -152,7 +152,7 @@ type ComplexityRoot struct {
|
||||
MyAlbums func(childComplexity int, filter *models.Filter, onlyRoot *bool, showEmpty *bool, onlyWithFavorites *bool) int
|
||||
MyMedia func(childComplexity int, filter *models.Filter) int
|
||||
MyMediaGeoJSON func(childComplexity int) int
|
||||
MyTimeline func(childComplexity int, onlyFavorites *bool) int
|
||||
MyTimeline func(childComplexity int, limit *int, offset *int, onlyFavorites *bool) int
|
||||
MyUser func(childComplexity int) int
|
||||
Search func(childComplexity int, query string, limitMedia *int, limitAlbums *int) int
|
||||
ShareToken func(childComplexity int, token string, password *string) int
|
||||
@@ -272,7 +272,7 @@ type QueryResolver interface {
|
||||
MyMedia(ctx context.Context, filter *models.Filter) ([]*models.Media, error)
|
||||
Media(ctx context.Context, id int) (*models.Media, error)
|
||||
MediaList(ctx context.Context, ids []int) ([]*models.Media, error)
|
||||
MyTimeline(ctx context.Context, onlyFavorites *bool) ([]*models.TimelineGroup, error)
|
||||
MyTimeline(ctx context.Context, limit *int, offset *int, onlyFavorites *bool) ([]*models.TimelineGroup, error)
|
||||
MyMediaGeoJSON(ctx context.Context) (interface{}, error)
|
||||
MapboxToken(ctx context.Context) (*string, error)
|
||||
ShareToken(ctx context.Context, token string, password *string) (*models.ShareToken, error)
|
||||
@@ -950,7 +950,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
||||
return 0, false
|
||||
}
|
||||
|
||||
return e.complexity.Query.MyTimeline(childComplexity, args["onlyFavorites"].(*bool)), true
|
||||
return e.complexity.Query.MyTimeline(childComplexity, args["limit"].(*int), args["offset"].(*int), args["onlyFavorites"].(*bool)), true
|
||||
|
||||
case "Query.myUser":
|
||||
if e.complexity.Query.MyUser == nil {
|
||||
@@ -1400,7 +1400,7 @@ type Query {
|
||||
"Get a list of media by their ids, user must own the media or be admin"
|
||||
mediaList(ids: [ID!]!): [Media!]!
|
||||
|
||||
myTimeline(onlyFavorites: Boolean): [TimelineGroup!]!
|
||||
myTimeline(limit: Int, offset: Int, onlyFavorites: Boolean): [TimelineGroup!]!
|
||||
|
||||
"Get media owned by the logged in user, returned in GeoJson format"
|
||||
myMediaGeoJson: Any!
|
||||
@@ -2195,15 +2195,33 @@ func (ec *executionContext) field_Query_myMedia_args(ctx context.Context, rawArg
|
||||
func (ec *executionContext) field_Query_myTimeline_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
|
||||
var err error
|
||||
args := map[string]interface{}{}
|
||||
var arg0 *bool
|
||||
if tmp, ok := rawArgs["onlyFavorites"]; ok {
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("onlyFavorites"))
|
||||
arg0, err = ec.unmarshalOBoolean2ᚖbool(ctx, tmp)
|
||||
var arg0 *int
|
||||
if tmp, ok := rawArgs["limit"]; ok {
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("limit"))
|
||||
arg0, err = ec.unmarshalOInt2ᚖint(ctx, tmp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
args["onlyFavorites"] = arg0
|
||||
args["limit"] = arg0
|
||||
var arg1 *int
|
||||
if tmp, ok := rawArgs["offset"]; ok {
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("offset"))
|
||||
arg1, err = ec.unmarshalOInt2ᚖint(ctx, tmp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
args["offset"] = arg1
|
||||
var arg2 *bool
|
||||
if tmp, ok := rawArgs["onlyFavorites"]; ok {
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("onlyFavorites"))
|
||||
arg2, err = ec.unmarshalOBoolean2ᚖbool(ctx, tmp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
args["onlyFavorites"] = arg2
|
||||
return args, nil
|
||||
}
|
||||
|
||||
@@ -5231,7 +5249,7 @@ func (ec *executionContext) _Query_myTimeline(ctx context.Context, field graphql
|
||||
fc.Args = args
|
||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||
ctx = rctx // use context from middleware stack in children
|
||||
return ec.resolvers.Query().MyTimeline(rctx, args["onlyFavorites"].(*bool))
|
||||
return ec.resolvers.Query().MyTimeline(rctx, args["limit"].(*int), args["offset"].(*int), args["onlyFavorites"].(*bool))
|
||||
})
|
||||
if err != nil {
|
||||
ec.Error(ctx, err)
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func (r *queryResolver) MyTimeline(ctx context.Context, onlyFavorites *bool) ([]*models.TimelineGroup, error) {
|
||||
func (r *queryResolver) MyTimeline(ctx context.Context, limit *int, offset *int, onlyFavorites *bool) ([]*models.TimelineGroup, error) {
|
||||
user := auth.UserFromContext(ctx)
|
||||
if user == nil {
|
||||
return nil, auth.ErrUnauthorized
|
||||
@@ -33,6 +33,14 @@ func (r *queryResolver) MyTimeline(ctx context.Context, onlyFavorites *bool) ([]
|
||||
daysQuery.Where("media.id IN (?)", tx.Table("user_media_data").Select("user_media_data.media_id").Where("user_media_data.user_id = ?", user.ID).Where("user_media_data.favorite = 1"))
|
||||
}
|
||||
|
||||
if limit != nil {
|
||||
daysQuery.Limit(*limit)
|
||||
}
|
||||
|
||||
if offset != nil {
|
||||
daysQuery.Offset(*offset)
|
||||
}
|
||||
|
||||
rows, err := daysQuery.Group("albums.id, YEAR(media.date_shot), MONTH(media.date_shot), DAY(media.date_shot)").
|
||||
Order("media.date_shot DESC").
|
||||
Rows()
|
||||
|
||||
@@ -44,7 +44,7 @@ type Query {
|
||||
"Get a list of media by their ids, user must own the media or be admin"
|
||||
mediaList(ids: [ID!]!): [Media!]!
|
||||
|
||||
myTimeline(onlyFavorites: Boolean): [TimelineGroup!]!
|
||||
myTimeline(limit: Int, offset: Int, onlyFavorites: Boolean): [TimelineGroup!]!
|
||||
|
||||
"Get media owned by the logged in user, returned in GeoJson format"
|
||||
myMediaGeoJson: Any!
|
||||
|
||||
@@ -4,7 +4,9 @@ import { useQuery, gql } from '@apollo/client'
|
||||
import AlbumGallery from '../../components/albumGallery/AlbumGallery'
|
||||
import PropTypes from 'prop-types'
|
||||
import Layout from '../../Layout'
|
||||
import useURLParameters from '../../components/useURLParameters'
|
||||
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>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -5,7 +5,10 @@ import {
|
||||
ApolloLink,
|
||||
HttpLink,
|
||||
} from '@apollo/client'
|
||||
import { getMainDefinition } from '@apollo/client/utilities'
|
||||
import {
|
||||
getMainDefinition,
|
||||
offsetLimitPagination,
|
||||
} from '@apollo/client/utilities'
|
||||
import { onError } from '@apollo/client/link/error'
|
||||
import { WebSocketLink } from '@apollo/client/link/ws'
|
||||
|
||||
@@ -111,6 +114,25 @@ 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,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,
|
||||
|
||||
@@ -5,12 +5,13 @@ import TimelineGroupDate from './TimelineGroupDate'
|
||||
import styled from 'styled-components'
|
||||
import PresentView from '../photoGallery/presentView/PresentView'
|
||||
import { Loader } from 'semantic-ui-react'
|
||||
import useURLParameters from '../useURLParameters'
|
||||
import useURLParameters from '../../hooks/useURLParameters'
|
||||
import { FavoritesCheckbox } from '../AlbumFilter'
|
||||
import useScrollPagination from '../../hooks/useScrollPagination'
|
||||
|
||||
const MY_TIMELINE_QUERY = gql`
|
||||
query myTimeline($onlyFavorites: Boolean) {
|
||||
myTimeline(onlyFavorites: $onlyFavorites) {
|
||||
query myTimeline($onlyFavorites: Boolean, $limit: Int, $offset: Int) {
|
||||
myTimeline(onlyFavorites: $onlyFavorites, limit: $limit, offset: $offset) {
|
||||
album {
|
||||
id
|
||||
title
|
||||
@@ -123,10 +124,22 @@ const TimelineGallery = () => {
|
||||
})
|
||||
}, [activeIndex])
|
||||
|
||||
const { data, error, loading, refetch } = useQuery(MY_TIMELINE_QUERY, {
|
||||
variables: {
|
||||
onlyFavorites,
|
||||
},
|
||||
const { data, error, loading, refetch, fetchMore } = useQuery(
|
||||
MY_TIMELINE_QUERY,
|
||||
{
|
||||
variables: {
|
||||
onlyFavorites,
|
||||
offset: 0,
|
||||
limit: 50,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
const { containerElem, finished: finishedLoadingMore } = useScrollPagination({
|
||||
loading,
|
||||
fetchMore,
|
||||
data,
|
||||
getItems: data => data.myTimeline,
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
@@ -190,7 +203,14 @@ const TimelineGallery = () => {
|
||||
onlyFavorites={onlyFavorites}
|
||||
setOnlyFavorites={setOnlyFavorites}
|
||||
/>
|
||||
<GalleryWrapper>{timelineGroups}</GalleryWrapper>
|
||||
<GalleryWrapper ref={containerElem}>{timelineGroups}</GalleryWrapper>
|
||||
<Loader
|
||||
style={{ margin: '42px 0 24px 0' }}
|
||||
active={!finishedLoadingMore && !loading}
|
||||
inline="centered"
|
||||
>
|
||||
Loading more media
|
||||
</Loader>
|
||||
{presenting && (
|
||||
<PresentView
|
||||
media={
|
||||
|
||||
81
ui/src/hooks/useScrollPagination.js
Normal file
81
ui/src/hooks/useScrollPagination.js
Normal file
@@ -0,0 +1,81 @@
|
||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||
|
||||
const useScrollPagination = ({ loading, fetchMore, data, getItems }) => {
|
||||
const observer = useRef(null)
|
||||
const observerElem = useRef(null)
|
||||
const [finished, setFinished] = useState(false)
|
||||
|
||||
const reconfigureIntersectionObserver = () => {
|
||||
var options = {
|
||||
root: null,
|
||||
rootMargin: '-100% 0px 0px 0px',
|
||||
threshold: 0,
|
||||
}
|
||||
|
||||
// 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)) {
|
||||
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) {
|
||||
observer.current.observe(observerElem.current)
|
||||
}
|
||||
}
|
||||
|
||||
const containerElem = useCallback(node => {
|
||||
observerElem.current = node
|
||||
|
||||
// cleanup
|
||||
if (observer.current != null) {
|
||||
observer.current.disconnect()
|
||||
}
|
||||
|
||||
if (node != null) {
|
||||
reconfigureIntersectionObserver()
|
||||
}
|
||||
}, [])
|
||||
|
||||
// only observe when not loading
|
||||
useEffect(() => {
|
||||
if (observer.current != null) {
|
||||
if (loading) {
|
||||
observer.current.unobserve(observerElem.current)
|
||||
} else {
|
||||
observer.current.observe(observerElem.current)
|
||||
}
|
||||
}
|
||||
}, [loading])
|
||||
|
||||
// reconfigure observer if fetchMore function changes
|
||||
useEffect(() => {
|
||||
reconfigureIntersectionObserver()
|
||||
}, [fetchMore, data, finished])
|
||||
|
||||
return {
|
||||
containerElem,
|
||||
finished,
|
||||
}
|
||||
}
|
||||
|
||||
export default useScrollPagination
|
||||
Reference in New Issue
Block a user