Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 21 additions & 9 deletions components/pull-to-refresh.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
import { useRouter } from 'next/router'
import { useState, useRef, useEffect, useCallback, useMemo } from 'react'
import styles from './pull-to-refresh.module.css'
import NProgress from 'nprogress'

const REFRESH_THRESHOLD = 150
const PULL_TIMEOUT = 1500

export default function PullToRefresh ({ children, className }) {
const router = useRouter()
const [pullDistance, setPullDistance] = useState(0)
const [isPWA, setIsPWA] = useState(false)
const touchStartY = useRef(0)
const touchEndY = useRef(0)
const pullTimeoutRef = useRef(null)

const checkPWA = () => {
const androidPWA = window.matchMedia('(display-mode: standalone)').matches
Expand All @@ -22,6 +25,10 @@ export default function PullToRefresh ({ children, className }) {
document.body.style.marginTop = '0px'
touchStartY.current = 0
touchEndY.current = 0
if (pullTimeoutRef.current) {
clearTimeout(pullTimeoutRef.current)
pullTimeoutRef.current = null
}
}

useEffect(checkPWA, [])
Expand All @@ -30,6 +37,9 @@ export default function PullToRefresh ({ children, className }) {
// don't handle if the user is not scrolling from the top of the page, is not on a PWA or if we want Android's native PTR
if (!isPWA || window.scrollY > 0) return
touchStartY.current = e.touches[0].clientY
pullTimeoutRef.current = setTimeout(() => {
clearPullDistance()
}, PULL_TIMEOUT)
}, [isPWA])

const handleTouchMove = useCallback((e) => {
Expand All @@ -41,26 +51,31 @@ export default function PullToRefresh ({ children, className }) {
clearPullDistance()
return
}

touchEndY.current = e.touches[0].clientY
const distance = touchEndY.current - touchStartY.current
setPullDistance(distance)
document.body.style.marginTop = `${Math.max(0, Math.min(distance / 2, 25))}px`
if (distance > 0) {
e.preventDefault()
setPullDistance(distance)
document.body.style.marginTop = `${Math.max(0, Math.min(distance / 2, 25))}px`
} else {
clearPullDistance()
}
}, [isPWA])

const handleTouchEnd = useCallback(() => {
if (touchStartY.current === 0 || touchEndY.current === 0) return
if (touchEndY.current - touchStartY.current > REFRESH_THRESHOLD) {
router.push(router.asPath)
NProgress.done()
router.replace(router.asPath)
}
clearPullDistance()
}, [router])

useEffect(() => {
if (!isPWA) return
document.body.style.overscrollBehaviorY = 'contain'
document.addEventListener('touchstart', handleTouchStart)
document.addEventListener('touchmove', handleTouchMove)
document.addEventListener('touchstart', handleTouchStart, { passive: false })
document.addEventListener('touchmove', handleTouchMove, { passive: false })
document.addEventListener('touchend', handleTouchEnd)
return () => {
document.body.style.overscrollBehaviorY = ''
Expand All @@ -79,9 +94,6 @@ export default function PullToRefresh ({ children, className }) {
return (
<main
className={className}
onTouchStart={handleTouchStart}
onTouchMove={handleTouchMove}
onTouchEnd={handleTouchEnd}
>
<p
className={`${styles.pullMessage}`}
Expand Down