From 6b8820b7abee0e5c5e5545abbf3b77f457356d06 Mon Sep 17 00:00:00 2001 From: SatsAllDay <128755788+SatsAllDay@users.noreply.github.com> Date: Thu, 5 Oct 2023 20:32:54 -0400 Subject: [PATCH] Fix quote selection reply in iOS Safari (#544) * Handle quote reply of selections in iOS Safari Approach borrowed from https://stackoverflow.com/a/72537632 Basically this makes a copy of the selection when the "touchend" event occurs, so we can use it for processing later This code listens to that event for each instance of the reply component, removing the event listener on unmount * Update docker-compose up command in dev notes --------- Co-authored-by: Keyan <34140557+huumn@users.noreply.github.com> --- components/reply.js | 33 ++++++++++++++++++++++++++++++--- docs/useful-dev-commands.md | 2 +- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/components/reply.js b/components/reply.js index 9bdd16bf..8e41d968 100644 --- a/components/reply.js +++ b/components/reply.js @@ -40,15 +40,42 @@ export default forwardRef(function Reply ({ item, onSuccess, replyOpen, children const parentId = item.id const replyInput = useRef(null) const formInnerRef = useRef() + + // Start block to handle iOS Safari's weird selection clearing behavior + const savedRange = useRef() + const savedRangeNode = useRef() + const onTouchEnd = useCallback(() => { + const selection = document.getSelection() + if (!selection || selection.rangeCount === 0 || selection.getRangeAt(0).length === 0) { + return + } + const range = selection.getRangeAt(0) + savedRangeNode.current = range.commonAncestorContainer + savedRange.current = range.cloneContents() + }, []) + useEffect(() => { + document.addEventListener('touchend', onTouchEnd) + return () => document.removeEventListener('touchend', onTouchEnd) + }, []) + // End block to handle iOS Safari's weird selection clearing behavior + useImperativeHandle(ref, () => ({ quoteReply: ({ selectionOnly }) => { if (!reply) { setReply(true) } const selection = window.getSelection() - const selectedText = selection.isCollapsed ? undefined : selection.toString() - const isSelectedTextInTarget = contentContainerRef?.current?.contains(selection.anchorNode) - if ((selection.isCollapsed || !isSelectedTextInTarget) && selectionOnly) return + let selectedText = selection.isCollapsed ? undefined : selection.toString() + let isSelectedTextInTarget = contentContainerRef?.current?.contains(selection.anchorNode) + + // Start block to handle iOS Safari's weird selection clearing behavior + if (!selectedText && savedRange.current && savedRangeNode.current) { + selectedText = savedRange.current.textContent + isSelectedTextInTarget = contentContainerRef?.current?.contains(savedRangeNode.current) + } + // End block to handle iOS Safari's weird selection clearing behavior + + if ((selection.isCollapsed || !isSelectedTextInTarget || !selectedText) && selectionOnly) return const textToQuote = isSelectedTextInTarget ? selectedText : item.text let updatedValue if (formInnerRef.current && formInnerRef.current.values && !formInnerRef.current.values.text) { diff --git a/docs/useful-dev-commands.md b/docs/useful-dev-commands.md index 265f7594..ca08b130 100644 --- a/docs/useful-dev-commands.md +++ b/docs/useful-dev-commands.md @@ -3,7 +3,7 @@ ### `nvm use 18` Switch to use nodejs version 18 in your current shell -### `docker-compose up -d` +### `docker-compose up --build -d` Bring up stacker news app via local docker services ### `docker-compose down`