enhance: cleanup, ux/ui changes, safer approach to android's ptr
This commit is contained in:
parent
5a359feeed
commit
a992426058
@ -2,12 +2,10 @@ import { useRouter } from 'next/router'
|
|||||||
import { useState, useRef, useEffect, useCallback } from 'react'
|
import { useState, useRef, useEffect, useCallback } from 'react'
|
||||||
import styles from './pull-to-refresh.module.css'
|
import styles from './pull-to-refresh.module.css'
|
||||||
|
|
||||||
const PULL_THRESHOLD = 300
|
const REFRESH_THRESHOLD = 300
|
||||||
const REFRESH_TIMEOUT = 500
|
|
||||||
|
|
||||||
export default function PullToRefresh ({ children, android }) {
|
export default function PullToRefresh ({ children, android }) {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const [isRefreshing, setIsRefreshing] = useState(false)
|
|
||||||
const [pullDistance, setPullDistance] = useState(0)
|
const [pullDistance, setPullDistance] = useState(0)
|
||||||
const [isPWA, setIsPWA] = useState(false)
|
const [isPWA, setIsPWA] = useState(false)
|
||||||
const [isAndroid, setIsAndroid] = useState(false)
|
const [isAndroid, setIsAndroid] = useState(false)
|
||||||
@ -21,9 +19,7 @@ export default function PullToRefresh ({ children, android }) {
|
|||||||
setIsPWA(androidPWA || iosPWA)
|
setIsPWA(androidPWA || iosPWA)
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(checkPWA, [])
|
||||||
checkPWA()
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
const handleTouchStart = useCallback((e) => {
|
const handleTouchStart = useCallback((e) => {
|
||||||
// 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
|
// 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
|
||||||
@ -32,19 +28,15 @@ export default function PullToRefresh ({ children, android }) {
|
|||||||
}, [isPWA, isAndroid, android])
|
}, [isPWA, isAndroid, android])
|
||||||
|
|
||||||
const handleTouchMove = useCallback((e) => {
|
const handleTouchMove = useCallback((e) => {
|
||||||
if (touchStartY.current === 0) return
|
if (touchStartY.current === 0) return // prevent unintended refresh by checking if the user has actually started touching
|
||||||
touchEndY.current = e.touches[0].clientY
|
touchEndY.current = e.touches[0].clientY
|
||||||
setPullDistance(touchEndY.current - touchStartY.current)
|
setPullDistance(touchEndY.current - touchStartY.current)
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const handleTouchEnd = useCallback(() => {
|
const handleTouchEnd = useCallback(() => {
|
||||||
if (touchStartY.current === 0 || touchEndY.current === 0) return
|
if (touchStartY.current === 0 || touchEndY.current === 0) return // if the user has started touch or is actually 'pulling'
|
||||||
if (touchEndY.current - touchStartY.current > PULL_THRESHOLD) {
|
if (touchEndY.current - touchStartY.current > REFRESH_THRESHOLD) {
|
||||||
setIsRefreshing(true)
|
|
||||||
router.push(router.asPath) // reload the same path
|
router.push(router.asPath) // reload the same path
|
||||||
setTimeout(() => {
|
|
||||||
setIsRefreshing(false)
|
|
||||||
}, REFRESH_TIMEOUT) // simulate loading time
|
|
||||||
}
|
}
|
||||||
setPullDistance(0) // using this to reset the message behavior
|
setPullDistance(0) // using this to reset the message behavior
|
||||||
touchStartY.current = 0 // avoid random refreshes by resetting touch
|
touchStartY.current = 0 // avoid random refreshes by resetting touch
|
||||||
@ -54,10 +46,12 @@ export default function PullToRefresh ({ children, android }) {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// don't handle if the user is not on a PWA or if we want Android's native PTR
|
// don't handle if the user is not on a PWA or if we want Android's native PTR
|
||||||
if (!isPWA || (isAndroid && !android)) return
|
if (!isPWA || (isAndroid && !android)) return
|
||||||
|
document.body.style.overscrollBehaviorY = 'contain' // disable Android's native PTR
|
||||||
document.addEventListener('touchstart', handleTouchStart)
|
document.addEventListener('touchstart', handleTouchStart)
|
||||||
document.addEventListener('touchmove', handleTouchMove)
|
document.addEventListener('touchmove', handleTouchMove)
|
||||||
document.addEventListener('touchend', handleTouchEnd)
|
document.addEventListener('touchend', handleTouchEnd)
|
||||||
return () => {
|
return () => {
|
||||||
|
document.body.style.overscrollBehaviorY = '' // if unmounted reset the overscroll behavior
|
||||||
document.removeEventListener('touchstart', handleTouchStart)
|
document.removeEventListener('touchstart', handleTouchStart)
|
||||||
document.removeEventListener('touchmove', handleTouchMove)
|
document.removeEventListener('touchmove', handleTouchMove)
|
||||||
document.removeEventListener('touchend', handleTouchEnd)
|
document.removeEventListener('touchend', handleTouchEnd)
|
||||||
@ -65,29 +59,21 @@ export default function PullToRefresh ({ children, android }) {
|
|||||||
}, [isPWA, isAndroid, android, handleTouchStart, handleTouchMove, handleTouchEnd])
|
}, [isPWA, isAndroid, android, handleTouchStart, handleTouchMove, handleTouchEnd])
|
||||||
|
|
||||||
const getPullMessage = () => {
|
const getPullMessage = () => {
|
||||||
if (isRefreshing) return 'refreshing...'
|
if (pullDistance > REFRESH_THRESHOLD) return 'release to refresh'
|
||||||
if (pullDistance > PULL_THRESHOLD) return 'release to refresh'
|
|
||||||
if (pullDistance > 0) return 'pull down to refresh'
|
if (pullDistance > 0) return 'pull down to refresh'
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={android ? styles.pullToRefreshContainer : ''}> {/* android prop if true disables its native PTR */}
|
<div>
|
||||||
<div
|
<div
|
||||||
onTouchStart={handleTouchStart}
|
onTouchStart={handleTouchStart}
|
||||||
onTouchMove={handleTouchMove}
|
onTouchMove={handleTouchMove}
|
||||||
onTouchEnd={handleTouchEnd}
|
onTouchEnd={handleTouchEnd}
|
||||||
>
|
>
|
||||||
{pullDistance > 0 || isRefreshing
|
<p className={`${styles.pullMessage} ${pullDistance > 50 && styles.fadeIn}`}>
|
||||||
? (
|
|
||||||
<>
|
|
||||||
<p className={`${styles.pullMessage} ${pullDistance > 50 || isRefreshing ? styles.fadeIn : ''}`}>
|
|
||||||
{getPullMessage()}
|
{getPullMessage()}
|
||||||
</p>
|
</p>
|
||||||
{isRefreshing && <div className={styles.spacer} />}
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
: null}
|
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,13 +1,9 @@
|
|||||||
.pullToRefreshContainer {
|
|
||||||
overscroll-behavior: contain;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pullMessage {
|
.pullMessage {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
top: -50px;
|
top: -50px;
|
||||||
transform: translateX(-50%);
|
transform: translateX(-50%);
|
||||||
font-size: 18px;
|
font-size: small;
|
||||||
color: #a5a5a5;
|
color: #a5a5a5;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transition: opacity 0.3s ease-in-out;
|
transition: opacity 0.3s ease-in-out;
|
||||||
@ -18,8 +14,3 @@
|
|||||||
opacity: 1;
|
opacity: 1;
|
||||||
top: 0;
|
top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.spacer {
|
|
||||||
position: relative;
|
|
||||||
margin-bottom: 32px;
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user