Fix first zap when modal closed (#771) (#1055)

* Fix first zap when modal closed (#771)

 - Extract handlers
 - Remove unnecessary async keyword from callback
 - Assign a new key to force remounting of LongPressable component when modal is closed from long press
 - Remove hover state when closing modal, otherwise it stays colored

* Replace LongPressable with custom component

* Remove yarn.lock
This commit is contained in:
✨JP⚡ 2024-04-12 19:37:04 -04:00 committed by GitHub
parent b477f23aac
commit a28d690f28
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 155 additions and 47 deletions

View File

@ -0,0 +1,114 @@
import React from 'react'
import PropType from 'prop-types'
function eventToPosition (event) {
return {
x: event.clientX,
y: event.clientY
}
}
function distance (pointA, pointB) {
return Math.sqrt(
Math.pow(pointB.x - pointA.x, 2) + Math.pow(pointB.y - pointA.y, 2)
)
}
export default class LongPressable extends React.PureComponent {
static propTypes = {
onLongPress: PropType.func.isRequired,
onShortPress: PropType.func,
longPressTime: PropType.number,
primaryMouseButtonOnly: PropType.bool,
// Maximum distance (pixels) user is allowed to drag before
// click is canceled
dragThreshold: PropType.number,
children: PropType.node
}
static defaultProps = {
longPressTime: 500,
primaryMouseButtonOnly: true,
dragThreshold: 100
}
isLongPressing = false
startingPosition = { x: 0, y: 0 }
componentWillUnmount () {
this.clearTimeout()
}
clearTimeout = () => {
if (this.timerID) {
clearTimeout(this.timerID)
this.timerID = null
}
}
handlePointerUp = (e) => {
if (this.timerID) {
this.cancelLongPress()
}
const mousePosition = eventToPosition(e)
if (!this.isLongPressing &&
!this.exceedDragThreshold(mousePosition)) {
this.props.onShortPress(e)
} else {
this.isLongPressing = false
}
}
handlePointerDown = (e) => {
if (this.props.primaryMouseButtonOnly) {
if (e.pointerType === 'mouse' && e.button !== 0) {
return
}
}
// re-initialize long press
this.isLongPressing = false
this.startingPosition = eventToPosition(e)
this.timerID = setTimeout(() => {
this.isLongPressing = true
this.props.onLongPress(e)
}, this.props.longPressTime)
}
handlePointerMove = (e) => {
const mousePosition = eventToPosition(e)
if (this.timerID && this.exceedDragThreshold(mousePosition)) {
this.cancelLongPress()
}
}
handlePointerLeave = () => {
if (this.timerID) {
this.cancelLongPress()
}
}
cancelLongPress () {
this.clearTimeout()
}
exceedDragThreshold (point) {
return distance(this.startingPosition, point) > this.props.dragThreshold
}
render () {
return (
<div
onPointerUp={this.handlePointerUp}
onPointerDown={this.handlePointerDown}
onPointerMove={this.handlePointerMove}
onPointerLeave={this.handlePointerLeave}
>
{this.props.children}
</div>
)
}
}

View File

@ -6,7 +6,7 @@ import ItemAct, { useAct, useZap } from './item-act'
import { useMe } from './me' import { useMe } from './me'
import getColor from '@/lib/rainbow' import getColor from '@/lib/rainbow'
import { useCallback, useMemo, useRef, useState } from 'react' import { useCallback, useMemo, useRef, useState } from 'react'
import LongPressable from 'react-longpressable' import LongPressable from './long-pressable'
import Overlay from 'react-bootstrap/Overlay' import Overlay from 'react-bootstrap/Overlay'
import Popover from 'react-bootstrap/Popover' import Popover from 'react-bootstrap/Popover'
import { useShowModal } from './modal' import { useShowModal } from './modal'
@ -142,11 +142,11 @@ export default function UpVote ({ item, className }) {
getColor(meSats), getColor(meSats + sats)] getColor(meSats), getColor(meSats + sats)]
}, [item?.meSats, item?.meAnonSats, me?.privates?.tipDefault, me?.privates?.turboDefault]) }, [item?.meSats, item?.meAnonSats, me?.privates?.tipDefault, me?.privates?.turboDefault])
return ( const handleModalClosed = () => {
<div ref={ref} className='upvoteParent'> setHover(false)
<LongPressable }
onLongPress={
async (e) => { const handleLongPress = (e) => {
if (!item) return if (!item) return
// we can't tip ourselves // we can't tip ourselves
@ -156,12 +156,11 @@ export default function UpVote ({ item, className }) {
setTipShow(false) setTipShow(false)
showModal(onClose => showModal(onClose =>
<ItemAct onClose={onClose} itemId={item.id} />) <ItemAct onClose={onClose} itemId={item.id} />, { onClose: handleModalClosed })
} }
}
onShortPress={ const handleShortPress = () => {
me if (me) {
? async (e) => {
if (!item) return if (!item) return
// we can't tip ourselves // we can't tip ourselves
@ -176,9 +175,15 @@ export default function UpVote ({ item, className }) {
} }
zap({ item, me }) zap({ item, me })
} else {
showModal(onClose => <ItemAct onClose={onClose} itemId={item.id} act={act} />, { onClose: handleModalClosed })
} }
: () => showModal(onClose => <ItemAct onClose={onClose} itemId={item.id} act={act} />)
} }
return (
<div ref={ref} className='upvoteParent'>
<LongPressable
onLongPress={handleLongPress}
onShortPress={handleShortPress}
> >
<ActionTooltip notForm disable={disabled} overlayText={overlayText}> <ActionTooltip notForm disable={disabled} overlayText={overlayText}>
<div <div

10
package-lock.json generated
View File

@ -69,7 +69,6 @@
"react-datepicker": "^4.20.0", "react-datepicker": "^4.20.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-ios-pwa-prompt": "^1.8.4", "react-ios-pwa-prompt": "^1.8.4",
"react-longpressable": "^1.1.1",
"react-markdown": "^9.0.1", "react-markdown": "^9.0.1",
"react-string-replace": "^1.1.1", "react-string-replace": "^1.1.1",
"react-syntax-highlighter": "^15.5.0", "react-syntax-highlighter": "^15.5.0",
@ -16164,15 +16163,6 @@
"resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
"integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
}, },
"node_modules/react-longpressable": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/react-longpressable/-/react-longpressable-1.1.1.tgz",
"integrity": "sha512-Q8S7CzZVNmP123tHrMp0U0+/fgDEZCi5CpOGkabz3a2zQ0aek5IAizetxtxBAt1hQHHaAYynPHhtcSkLIkqEzQ==",
"peerDependencies": {
"prop-types": "^15.6.1",
"react": "^16.4.0"
}
},
"node_modules/react-markdown": { "node_modules/react-markdown": {
"version": "9.0.1", "version": "9.0.1",
"resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-9.0.1.tgz", "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-9.0.1.tgz",

View File

@ -74,7 +74,6 @@
"react-datepicker": "^4.20.0", "react-datepicker": "^4.20.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-ios-pwa-prompt": "^1.8.4", "react-ios-pwa-prompt": "^1.8.4",
"react-longpressable": "^1.1.1",
"react-markdown": "^9.0.1", "react-markdown": "^9.0.1",
"react-string-replace": "^1.1.1", "react-string-replace": "^1.1.1",
"react-syntax-highlighter": "^15.5.0", "react-syntax-highlighter": "^15.5.0",