Skip to content

Note: Prevent programmatic selection on tap #2904

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

Merged
Merged
Show file tree
Hide file tree
Changes from 4 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
2 changes: 2 additions & 0 deletions src/actions/setCursor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import expandThoughts from '../selectors/expandThoughts'
import getSetting from '../selectors/getSetting'
import getThoughtById from '../selectors/getThoughtById'
import simplifyPath from '../selectors/simplifyPath'
import { SetCursorActionMetadata } from '../stores/actionMetadata'
import editingValueStore from '../stores/editingValue'
import equalPath from '../util/equalPath'
import head from '../util/head'
Expand All @@ -44,6 +45,7 @@ const setCursor = (
cursorHistoryClear?: boolean
cursorHistoryPop?: boolean
editing?: boolean | null
metadata?: SetCursorActionMetadata
noteFocus?: boolean
offset?: number | null
path: Path | null
Expand Down
6 changes: 6 additions & 0 deletions src/components/Note.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { toggleNoteActionCreator as toggleNote } from '../actions/toggleNote'
import { isSafari, isTouch } from '../browser'
import * as selection from '../device/selection'
import useFreshCallback from '../hooks/useFreshCallback'
import actionMetadataStore from '../stores/actionMetadata'
import store from '../stores/app'
import equalPathHead from '../util/equalPathHead'
import head from '../util/head'
Expand Down Expand Up @@ -48,6 +49,7 @@ const Note = React.memo(
path,
cursorHistoryClear: true,
editing: true,
metadata: { userGenerated: true },
noteFocus: true,
}),
)
Expand All @@ -57,6 +59,10 @@ const Note = React.memo(
useEffect(() => {
// cursor must be true if note is focused
if (hasFocus) {
const actionMetadata = actionMetadataStore.getState()

if (actionMetadata?.type === 'setCursor' && actionMetadata.userGenerated) return

selection.set(noteRef.current!, { end: true })
// deleting a note, then closing the keyboard, then creating a new note could result in lack of focus,
// perhaps related to iOS Safari's internal management of selection ranges and focus
Expand Down
27 changes: 27 additions & 0 deletions src/redux-middleware/updateActionMetadata.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { ThunkMiddleware } from 'redux-thunk'
import State from '../@types/State'
import actionMetadataStore, { ActionMetadata, SetCursorActionMetadata } from '../stores/actionMetadata'

interface SetCursorActionWithMetadata {
metadata?: SetCursorActionMetadata
type: 'setCursor'
}

// This can eventually be a discriminated union
export type ActionWithMetadata = SetCursorActionWithMetadata

/** Casts an unknown action to an action with metadata. Needs some more work to make it flexible enough. */
const hasMetadata = (action: unknown): action is ActionWithMetadata => !!action && action.hasOwnProperty('metadata')

/** Action metadata should be ephemeral and must be updated for every action.
* Update the actionMetadata store on every action, even if metadata is undefined. */
const updateActionMetadata: ThunkMiddleware<State> = () => {
return next => action => {
actionMetadataStore.update(
hasMetadata(action) ? ({ type: action.type, ...action.metadata } as ActionMetadata) : { type: '' },
)
next(action)
}
}

export default updateActionMetadata
13 changes: 13 additions & 0 deletions src/stores/actionMetadata.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import reactMinistore from './react-ministore'

export type SetCursorActionMetadata = {
userGenerated: boolean
}

// This can eventually be a discriminated union
export type ActionMetadata = (SetCursorActionMetadata & { type: 'setCursor' }) | { type: '' }

/** A store that hold optional metadata for the most-recently-dispatched action. */
const actionMetadataStore = reactMinistore<ActionMetadata>({ type: '' })

export default actionMetadataStore
2 changes: 2 additions & 0 deletions src/stores/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import multi from '../redux-middleware/multi'
import multicursorAlertMiddleware from '../redux-middleware/multicursorAlertMiddleware'
import pullQueue from '../redux-middleware/pullQueue'
import scrollCursorIntoView from '../redux-middleware/scrollCursorIntoView'
import updateActionMetadata from '../redux-middleware/updateActionMetadata'
import updateEditingValue from '../redux-middleware/updateEditingValue'
import updateUrlHistory from '../redux-middleware/updateUrlHistory'

Expand All @@ -40,6 +41,7 @@ const middlewareEnhancer = applyMiddleware(
pullQueue,
scrollCursorIntoView,
clearSelection,
updateActionMetadata,
updateEditingValue,
updateUrlHistory,
freeThoughts,
Expand Down
Loading