From 08a20e25b416e672f0e80518ca66365f7089b4f3 Mon Sep 17 00:00:00 2001 From: Aman Harwara Date: Thu, 30 Mar 2023 00:35:55 +0530 Subject: [PATCH] fix: Added backspace button on Super toolbar on Android as a workaround for backspace issue --- packages/icons/src/Icons/ic-backspace.svg | 4 ++ packages/icons/src/Icons/index.ts | 16 +++-- packages/styles/src/Alert/Alert.ts | 6 +- .../Components/Icon/IconNameToSvgMapping.tsx | 1 + .../MobileToolbarPlugin.tsx | 66 +++++++++++++++++-- 5 files changed, 80 insertions(+), 13 deletions(-) create mode 100644 packages/icons/src/Icons/ic-backspace.svg diff --git a/packages/icons/src/Icons/ic-backspace.svg b/packages/icons/src/Icons/ic-backspace.svg new file mode 100644 index 000000000..7b1116608 --- /dev/null +++ b/packages/icons/src/Icons/ic-backspace.svg @@ -0,0 +1,4 @@ + + + \ No newline at end of file diff --git a/packages/icons/src/Icons/index.ts b/packages/icons/src/Icons/index.ts index 63a0a3678..5a32917e5 100644 --- a/packages/icons/src/Icons/index.ts +++ b/packages/icons/src/Icons/index.ts @@ -6,6 +6,7 @@ import AccountVariantIcon from './ic-account-variant.svg' import AddBoldIcon from './ic-add-bold.svg' import AddIcon from './ic-add.svg' import AddTextIcon from './ic-add-text.svg' +import AegisIcon from './ic-aegis.svg' import ArchiveIcon from './ic-archive.svg' import ArrowDownCheckmarkIcon from './arrow-down-checkmark.svg' import ArrowDownIcon from './ic-arrow-down.svg' @@ -21,6 +22,7 @@ import AttachmentFileIcon from './ic-attachment-file.svg' import AuthenticatorIcon from './ic-authenticator.svg' import AuthenticatorVariantIcon from './ic-authenticator-variant.svg' import BackIosIcon from './ic-back-ios.svg' +import BackspaceIcon from './ic-backspace.svg' import BlockIcon from './ic-block.svg' import BlueDotIcon from './blue-dot.svg' import BoldIcon from './ic-bold.svg' @@ -57,6 +59,7 @@ import EditorIcon from './ic-editor.svg' import EmailFilledIcon from './ic-email-filled.svg' import EmailIcon from './ic-email.svg' import EnterIcon from './ic-enter.svg' +import EvernoteIcon from './ic-evernote.svg' import EyeFilledIcon from './ic-eye-filled.svg' import EyeIcon from './ic-eye.svg' import EyeOffFilledIcon from './ic-eye-off-filled.svg' @@ -87,6 +90,7 @@ import ForwardIosIcon from './ic-forward-ios.svg' import FullscreenExitIcon from './ic-fullscreen-exit.svg' import FullscreenIcon from './ic-fullscreen.svg' import GiftOutlineIcon from './ic-gift-outline.svg' +import GoogleKeepIcon from './ic-gkeep.svg' import HashtagFilledIcon from './ic-hashtag-filled.svg' import HashtagIcon from './ic-hashtag.svg' import HashtagOffIcon from './ic-hashtag-off.svg' @@ -112,9 +116,9 @@ import LineWidthIcon from './ic-line-width.svg' import LinkIcon from './ic-link.svg' import LinkOffIcon from './ic-link-off.svg' import ListBulleted from './ic-list-bulleted.svg' -import ListNumbered from './ic-list-numbered.svg' import ListedFilledIcon from './ic-listed-filled.svg' import ListedIcon from './ic-listed.svg' +import ListNumbered from './ic-list-numbered.svg' import LockFilledIcon from './ic-lock-filled.svg' import LockIcon from './ic-lock.svg' import MarkdownIcon from './ic-markdown.svg' @@ -146,6 +150,8 @@ import PrintIcon from './ic-print.svg' import ProtectedIllustration from './il-protected.svg' import RedoIcon from './ic-redo.svg' import ReorderIcon from './ic-reorder.svg' +import ReplaceAllIcon from './ic-replace-all.svg' +import ReplaceIcon from './ic-replace.svg' import RestoreIcon from './ic-restore.svg' import RichTextIcon from './ic-text-rich.svg' import SafeIcon from './ic-safe.svg' @@ -164,6 +170,7 @@ import ShareIcon from './ic-share.svg' import ShortcutButtonIcon from './ic-shortcut-button.svg' import SignInIcon from './ic-signin.svg' import SignOutIcon from './ic-signout.svg' +import SimplenoteIcon from './ic-simplenote.svg' import SNLogoAltIcon from './ic-standard-notes-2.svg' import SNLogoFull from './ic-sn-logo-full.svg' import SNLogoIcon from './ic-standard-notes.svg' @@ -201,12 +208,6 @@ import UserSwitch from './ic-user-switch.svg' import ViewIcon from './ic-view.svg' import WarningIcon from './ic-warning.svg' import WindowIcon from './ic-window.svg' -import EvernoteIcon from './ic-evernote.svg' -import GoogleKeepIcon from './ic-gkeep.svg' -import SimplenoteIcon from './ic-simplenote.svg' -import AegisIcon from './ic-aegis.svg' -import ReplaceIcon from './ic-replace.svg' -import ReplaceAllIcon from './ic-replace-all.svg' export { AccessibilityIcon, @@ -232,6 +233,7 @@ export { AuthenticatorIcon, AuthenticatorVariantIcon, BackIosIcon, + BackspaceIcon, BlockIcon, BlueDotIcon, BoldIcon, diff --git a/packages/styles/src/Alert/Alert.ts b/packages/styles/src/Alert/Alert.ts index e9de199a5..8d796f117 100644 --- a/packages/styles/src/Alert/Alert.ts +++ b/packages/styles/src/Alert/Alert.ts @@ -41,7 +41,7 @@ const getColorsForPrimaryVariant = (style: AlertButtonStyle) => { type AlertButton = { text: string style: AlertButtonStyle - action: () => void + action?: () => void primary?: boolean } @@ -168,7 +168,9 @@ export class SKAlert { this.buttons.forEach((buttonDesc, index) => { const buttonElem = this.element.querySelector(`#button-${index}`) as HTMLButtonElement buttonElem.onclick = () => { - buttonDesc.action && buttonDesc.action() + if (buttonDesc.action) { + buttonDesc.action() + } this.dismiss() } if (index === 0) { diff --git a/packages/web/src/javascripts/Components/Icon/IconNameToSvgMapping.tsx b/packages/web/src/javascripts/Components/Icon/IconNameToSvgMapping.tsx index c8d382ade..7312642ba 100644 --- a/packages/web/src/javascripts/Components/Icon/IconNameToSvgMapping.tsx +++ b/packages/web/src/javascripts/Components/Icon/IconNameToSvgMapping.tsx @@ -73,6 +73,7 @@ export const IconNameToSvgMapping = { archive: icons.ArchiveIcon, asterisk: icons.AsteriskIcon, authenticator: icons.AuthenticatorIcon, + backspace: icons.BackspaceIcon, bold: icons.BoldIcon, camera: icons.CameraIcon, check: icons.CheckIcon, diff --git a/packages/web/src/javascripts/Components/SuperEditor/Plugins/MobileToolbarPlugin/MobileToolbarPlugin.tsx b/packages/web/src/javascripts/Components/SuperEditor/Plugins/MobileToolbarPlugin/MobileToolbarPlugin.tsx index daddda25d..050ee6149 100644 --- a/packages/web/src/javascripts/Components/SuperEditor/Plugins/MobileToolbarPlugin/MobileToolbarPlugin.tsx +++ b/packages/web/src/javascripts/Components/SuperEditor/Plugins/MobileToolbarPlugin/MobileToolbarPlugin.tsx @@ -4,7 +4,7 @@ import useModal from '../../Lexical/Hooks/useModal' import { InsertTableDialog } from '../../Plugins/TablePlugin' import { getSelectedNode } from '../../Lexical/Utils/getSelectedNode' import { sanitizeUrl } from '../../Lexical/Utils/sanitizeUrl' -import { $getSelection, $isRangeSelection, FORMAT_TEXT_COMMAND } from 'lexical' +import { $getSelection, $isRangeSelection, DELETE_CHARACTER_COMMAND, FORMAT_TEXT_COMMAND } from 'lexical' import { $isLinkNode, TOGGLE_LINK_COMMAND } from '@lexical/link' import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { GetAlignmentBlocks } from '../Blocks/Alignment' @@ -23,11 +23,14 @@ import { GetPasswordBlock } from '../Blocks/Password' import { GetQuoteBlock } from '../Blocks/Quote' import { GetTableBlock } from '../Blocks/Table' import { MutuallyExclusiveMediaQueryBreakpoints, useMediaQuery } from '@/Hooks/useMediaQuery' -import { classNames } from '@standardnotes/snjs' +import { classNames, Platform } from '@standardnotes/snjs' import { SUPER_TOGGLE_SEARCH } from '@standardnotes/ui-services' import { useApplication } from '@/Components/ApplicationProvider' import { GetRemoteImageBlock } from '../Blocks/RemoteImage' import { InsertRemoteImageDialog } from '../RemoteImagePlugin/RemoteImagePlugin' +import { SKAlert } from '@standardnotes/styles' + +const DontShowSuperAndroidBackspaceAlertKey = 'dontShowSuperAndroidBackspaceAlert' const MobileToolbarPlugin = () => { const application = useApplication() @@ -37,8 +40,10 @@ const MobileToolbarPlugin = () => { const [isInEditor, setIsInEditor] = useState(false) const [isInToolbar, setIsInToolbar] = useState(false) const isMobile = useMediaQuery(MutuallyExclusiveMediaQueryBreakpoints.sm) + const isAndroid = application.platform === Platform.Android const toolbarRef = useRef(null) + const backspaceButtonRef = useRef(null) const insertLink = useCallback(() => { const selection = $getSelection() @@ -156,7 +161,8 @@ const MobileToolbarPlugin = () => { const handleFocus = () => setIsInEditor(true) const handleBlur = (event: FocusEvent) => { - if (toolbarRef.current?.contains(event.relatedTarget as Node)) { + const elementToBeFocused = event.relatedTarget as Node + if (toolbarRef.current?.contains(elementToBeFocused) || elementToBeFocused === backspaceButtonRef.current) { return } setIsInEditor(false) @@ -179,7 +185,13 @@ const MobileToolbarPlugin = () => { const toolbar = toolbarRef.current const handleFocus = () => setIsInToolbar(true) - const handleBlur = () => setIsInToolbar(false) + const handleBlur = (event: FocusEvent) => { + const elementToBeFocused = event.relatedTarget as Node + if (elementToBeFocused === backspaceButtonRef.current) { + return + } + setIsInToolbar(false) + } toolbar.addEventListener('focus', handleFocus) toolbar.addEventListener('blur', handleBlur) @@ -190,6 +202,38 @@ const MobileToolbarPlugin = () => { } }, []) + useEffect(() => { + if (!isAndroid) { + return + } + + const dontShowAgain = application.getValue(DontShowSuperAndroidBackspaceAlertKey) + + if (dontShowAgain) { + return + } + + const alert = new SKAlert({ + title: 'Android backspace issue', + text: 'There is a known issue with Super on Android where pressing the backspace will also delete the character after the cursor. We are working on a fix for this. Please use the backspace button in the toolbar as a workaround.', + buttons: [ + { + text: 'OK', + style: 'default', + }, + { + text: "Don't show again", + style: 'default', + action: () => { + application.setValue(DontShowSuperAndroidBackspaceAlertKey, true) + }, + }, + ], + }) + + alert.present() + }, [application, isAndroid]) + const isFocusInEditorOrToolbar = isInEditor || isInToolbar if (!isMobile || !isFocusInEditorOrToolbar) { return null @@ -223,6 +267,20 @@ const MobileToolbarPlugin = () => { > + {isAndroid && ( + + )} )