Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bullet/Editable: Use fastClick for clickHandler #2752

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
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
12 changes: 7 additions & 5 deletions src/components/Bullet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import getThoughtById from '../selectors/getThoughtById'
import isContextViewActive from '../selectors/isContextViewActive'
import isMulticursorPath from '../selectors/isMulticursorPath'
import rootedParentOf from '../selectors/rootedParentOf'
import fastClick, { type FastClickEvent } from '../util/fastClick'
import hashPath from '../util/hashPath'
import head from '../util/head'
import isDivider from '../util/isDivider'
Expand Down Expand Up @@ -531,16 +532,15 @@ const Bullet = ({
// expand or collapse on click
// has some additional logic to make it work intuitively with pin true/false
const clickHandler = useCallback(
(e: React.MouseEvent) => {
// stop click event from bubbling up to Content.clickOnEmptySpace
e.stopPropagation()
(e: FastClickEvent) => {
// short circuit if dragHold
// useLongPress stop is activated in onMouseUp but is delayed to ensure that dragHold is still true here
// stopping propagation from useLongPress was not working either due to bubbling order or mismatched event type
if (dragHold) return

// short circuit if toggling multiselect
if (!isTouch && (isMac ? e.metaKey : e.ctrlKey)) {
// e should always be a MouseEvent if !isTouch, but getting the type system to believe it is another story
if (!isTouch && e instanceof MouseEvent && (isMac ? e.metaKey : e.ctrlKey)) {
dispatch(toggleMulticursor({ path }))
return
}
Expand Down Expand Up @@ -605,7 +605,9 @@ const Bullet = ({
paddingBottom: extendClickHeight + 2,
width,
}}
onClick={clickHandler}
{...fastClick(clickHandler)}
// stop click event from bubbling up to Content.clickOnEmptySpace
onClick={e => e.stopPropagation()}
>
<svg
className={cx(
Expand Down
8 changes: 5 additions & 3 deletions src/components/Editable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { setCursorActionCreator as setCursor } from '../actions/setCursor'
import { toggleColorPickerActionCreator as toggleColorPicker } from '../actions/toggleColorPicker'
import { toggleLetterCaseActionCreator as toggleLetterCase } from '../actions/toggleLetterCase'
import { tutorialNextActionCreator as tutorialNext } from '../actions/tutorialNext'
import { isMac, isTouch } from '../browser'
import { isMac, isSafari, isTouch } from '../browser'
import { commandEmitter } from '../commands'
import {
EDIT_THROTTLE,
Expand Down Expand Up @@ -203,6 +203,8 @@ const Editable = ({
// set offset to null to allow the browser to set the position of the selection
let offset = null

if (isTouch && isSafari() && contentRef.current?.innerHTML.length) offset = contentRef.current.innerHTML.length
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The caret was getting positioned at the end of the thought somehow onFocus, so I wanted to preserve that behavior onTap.


// if running for the first time, restore the offset if the path matches the restored cursor
if (!cursorOffsetInitialized) {
const restored: { path: Path | null; offset: number | null } = storageModel.get('cursor')
Expand Down Expand Up @@ -545,7 +547,7 @@ const Editable = ({
if (
disabled ||
// dragInProgress: not sure if this can happen, but I observed some glitchy behavior with the cursor moving when a drag and drop is completed so check dragInProgress to be safe
(!globals.touching && !state.dragInProgress && !state.dragHold && (!editingOrOnCursor || !isVisible))
(isTouch && isSafari() && !globals.touching && !state.dragInProgress && !state.dragHold)
) {
// do not set cursor on hidden thought
e.preventDefault()
Expand All @@ -557,7 +559,7 @@ const Editable = ({

if (state.showLetterCase) dispatch(toggleLetterCase({ value: false }))
} else {
setCursorOnThought()
setCursorOnThought({ editing: editingOrOnCursor })

// When the the cursor is first set on a thought, prevent the default browser behavior to avoid activating edit mode.
// Do not reset until the long tap is definitely over.
Expand Down
1 change: 1 addition & 0 deletions src/components/Editable/useEditMode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ const useEditMode = ({
selection.clear()
} else {
selection.set(contentRef.current, { offset: editingCursorOffset || 0 })
if (isTouch && isSafari()) contentRef.current?.focus()
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not quite sure why this is necessary, but it was having the following problem on iOS Safari:

  1. Tap a thought - cursor moves, keyboard stays closed
  2. Tap it again - keyboard opens
  3. Close keyboard
  4. Tap it again - keyboard does not open

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, good catch.

I'll create a separate issue to get to the bottom of this.

}
}

Expand Down
11 changes: 7 additions & 4 deletions src/util/fastClick.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import _ from 'lodash'
import React from 'react'
import { isTouch } from '../browser'

export type FastClickEvent = React.TouchEvent<Element> | React.MouseEvent<Element, MouseEvent>

// the number of pixels of scrolling or dragging from touchStart that is allowed to still trigger fastClick
const MOVE_THRESHOLD = 15

Expand All @@ -12,13 +15,13 @@ const fastClick = isTouch
? (
// triggered on mouseup or touchend
// cancelled if the user scroll or drags
tapUp: (e: React.TouchEvent) => void,
tapUp: (e: FastClickEvent) => void,
// triggered on mousedown or touchstart
tapDown?: (e: React.TouchEvent) => void,
tapDown?: (e: FastClickEvent) => void,
// triggered when tapUp is cancelled due to scrolling or dragging
// does not work with drag-and-drop on desktop (onMouseUp does not trigger)
tapCancel?: (e: React.TouchEvent) => void,
// triggered with touchMove
tapCancel?: (e: FastClickEvent) => void,
// triggered with touchMove, which can never be a MouseEvent
touchMove?: (e: React.TouchEvent) => void,
) => ({
onTouchStart: (e: React.TouchEvent) => {
Expand Down
Loading