* 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:
parent
b477f23aac
commit
a28d690f28
|
@ -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>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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,43 +142,48 @@ 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])
|
||||||
|
|
||||||
|
const handleModalClosed = () => {
|
||||||
|
setHover(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleLongPress = (e) => {
|
||||||
|
if (!item) return
|
||||||
|
|
||||||
|
// we can't tip ourselves
|
||||||
|
if (disabled) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
setTipShow(false)
|
||||||
|
showModal(onClose =>
|
||||||
|
<ItemAct onClose={onClose} itemId={item.id} />, { onClose: handleModalClosed })
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleShortPress = () => {
|
||||||
|
if (me) {
|
||||||
|
if (!item) return
|
||||||
|
|
||||||
|
// we can't tip ourselves
|
||||||
|
if (disabled) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (meSats) {
|
||||||
|
setVoteShow(false)
|
||||||
|
} else {
|
||||||
|
setTipShow(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
zap({ item, me })
|
||||||
|
} else {
|
||||||
|
showModal(onClose => <ItemAct onClose={onClose} itemId={item.id} act={act} />, { onClose: handleModalClosed })
|
||||||
|
}
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<div ref={ref} className='upvoteParent'>
|
<div ref={ref} className='upvoteParent'>
|
||||||
<LongPressable
|
<LongPressable
|
||||||
onLongPress={
|
onLongPress={handleLongPress}
|
||||||
async (e) => {
|
onShortPress={handleShortPress}
|
||||||
if (!item) return
|
|
||||||
|
|
||||||
// we can't tip ourselves
|
|
||||||
if (disabled) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
setTipShow(false)
|
|
||||||
showModal(onClose =>
|
|
||||||
<ItemAct onClose={onClose} itemId={item.id} />)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onShortPress={
|
|
||||||
me
|
|
||||||
? async (e) => {
|
|
||||||
if (!item) return
|
|
||||||
|
|
||||||
// we can't tip ourselves
|
|
||||||
if (disabled) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (meSats) {
|
|
||||||
setVoteShow(false)
|
|
||||||
} else {
|
|
||||||
setTipShow(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
zap({ item, me })
|
|
||||||
}
|
|
||||||
: () => showModal(onClose => <ItemAct onClose={onClose} itemId={item.id} act={act} />)
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<ActionTooltip notForm disable={disabled} overlayText={overlayText}>
|
<ActionTooltip notForm disable={disabled} overlayText={overlayText}>
|
||||||
<div
|
<div
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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",
|
||||||
|
@ -121,4 +120,4 @@
|
||||||
"jest": "^29.7.0",
|
"jest": "^29.7.0",
|
||||||
"standard": "^17.1.0"
|
"standard": "^17.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue