fix: Added backspace button on Super toolbar on Android as a workaround for backspace issue

This commit is contained in:
Aman Harwara
2023-03-30 00:35:55 +05:30
parent c3a5b249b2
commit 08a20e25b4
5 changed files with 80 additions and 13 deletions

View File

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path fill="currentColor"
d="m11.4 16l2.6-2.6l2.6 2.6l1.4-1.4l-2.6-2.6L18 9.4L16.6 8L14 10.6L11.4 8L10 9.4l2.6 2.6l-2.6 2.6l1.4 1.4ZM3 12l4.35-6.15q.275-.4.713-.625T9 5h10q.825 0 1.413.588T21 7v10q0 .825-.588 1.413T19 19H9q-.5 0-.938-.225t-.712-.625L3 12Z" />
</svg>

After

Width:  |  Height:  |  Size: 330 B

View File

@@ -6,6 +6,7 @@ import AccountVariantIcon from './ic-account-variant.svg'
import AddBoldIcon from './ic-add-bold.svg' import AddBoldIcon from './ic-add-bold.svg'
import AddIcon from './ic-add.svg' import AddIcon from './ic-add.svg'
import AddTextIcon from './ic-add-text.svg' import AddTextIcon from './ic-add-text.svg'
import AegisIcon from './ic-aegis.svg'
import ArchiveIcon from './ic-archive.svg' import ArchiveIcon from './ic-archive.svg'
import ArrowDownCheckmarkIcon from './arrow-down-checkmark.svg' import ArrowDownCheckmarkIcon from './arrow-down-checkmark.svg'
import ArrowDownIcon from './ic-arrow-down.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 AuthenticatorIcon from './ic-authenticator.svg'
import AuthenticatorVariantIcon from './ic-authenticator-variant.svg' import AuthenticatorVariantIcon from './ic-authenticator-variant.svg'
import BackIosIcon from './ic-back-ios.svg' import BackIosIcon from './ic-back-ios.svg'
import BackspaceIcon from './ic-backspace.svg'
import BlockIcon from './ic-block.svg' import BlockIcon from './ic-block.svg'
import BlueDotIcon from './blue-dot.svg' import BlueDotIcon from './blue-dot.svg'
import BoldIcon from './ic-bold.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 EmailFilledIcon from './ic-email-filled.svg'
import EmailIcon from './ic-email.svg' import EmailIcon from './ic-email.svg'
import EnterIcon from './ic-enter.svg' import EnterIcon from './ic-enter.svg'
import EvernoteIcon from './ic-evernote.svg'
import EyeFilledIcon from './ic-eye-filled.svg' import EyeFilledIcon from './ic-eye-filled.svg'
import EyeIcon from './ic-eye.svg' import EyeIcon from './ic-eye.svg'
import EyeOffFilledIcon from './ic-eye-off-filled.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 FullscreenExitIcon from './ic-fullscreen-exit.svg'
import FullscreenIcon from './ic-fullscreen.svg' import FullscreenIcon from './ic-fullscreen.svg'
import GiftOutlineIcon from './ic-gift-outline.svg' import GiftOutlineIcon from './ic-gift-outline.svg'
import GoogleKeepIcon from './ic-gkeep.svg'
import HashtagFilledIcon from './ic-hashtag-filled.svg' import HashtagFilledIcon from './ic-hashtag-filled.svg'
import HashtagIcon from './ic-hashtag.svg' import HashtagIcon from './ic-hashtag.svg'
import HashtagOffIcon from './ic-hashtag-off.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 LinkIcon from './ic-link.svg'
import LinkOffIcon from './ic-link-off.svg' import LinkOffIcon from './ic-link-off.svg'
import ListBulleted from './ic-list-bulleted.svg' import ListBulleted from './ic-list-bulleted.svg'
import ListNumbered from './ic-list-numbered.svg'
import ListedFilledIcon from './ic-listed-filled.svg' import ListedFilledIcon from './ic-listed-filled.svg'
import ListedIcon from './ic-listed.svg' import ListedIcon from './ic-listed.svg'
import ListNumbered from './ic-list-numbered.svg'
import LockFilledIcon from './ic-lock-filled.svg' import LockFilledIcon from './ic-lock-filled.svg'
import LockIcon from './ic-lock.svg' import LockIcon from './ic-lock.svg'
import MarkdownIcon from './ic-markdown.svg' import MarkdownIcon from './ic-markdown.svg'
@@ -146,6 +150,8 @@ import PrintIcon from './ic-print.svg'
import ProtectedIllustration from './il-protected.svg' import ProtectedIllustration from './il-protected.svg'
import RedoIcon from './ic-redo.svg' import RedoIcon from './ic-redo.svg'
import ReorderIcon from './ic-reorder.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 RestoreIcon from './ic-restore.svg'
import RichTextIcon from './ic-text-rich.svg' import RichTextIcon from './ic-text-rich.svg'
import SafeIcon from './ic-safe.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 ShortcutButtonIcon from './ic-shortcut-button.svg'
import SignInIcon from './ic-signin.svg' import SignInIcon from './ic-signin.svg'
import SignOutIcon from './ic-signout.svg' import SignOutIcon from './ic-signout.svg'
import SimplenoteIcon from './ic-simplenote.svg'
import SNLogoAltIcon from './ic-standard-notes-2.svg' import SNLogoAltIcon from './ic-standard-notes-2.svg'
import SNLogoFull from './ic-sn-logo-full.svg' import SNLogoFull from './ic-sn-logo-full.svg'
import SNLogoIcon from './ic-standard-notes.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 ViewIcon from './ic-view.svg'
import WarningIcon from './ic-warning.svg' import WarningIcon from './ic-warning.svg'
import WindowIcon from './ic-window.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 { export {
AccessibilityIcon, AccessibilityIcon,
@@ -232,6 +233,7 @@ export {
AuthenticatorIcon, AuthenticatorIcon,
AuthenticatorVariantIcon, AuthenticatorVariantIcon,
BackIosIcon, BackIosIcon,
BackspaceIcon,
BlockIcon, BlockIcon,
BlueDotIcon, BlueDotIcon,
BoldIcon, BoldIcon,

View File

@@ -41,7 +41,7 @@ const getColorsForPrimaryVariant = (style: AlertButtonStyle) => {
type AlertButton = { type AlertButton = {
text: string text: string
style: AlertButtonStyle style: AlertButtonStyle
action: () => void action?: () => void
primary?: boolean primary?: boolean
} }
@@ -168,7 +168,9 @@ export class SKAlert {
this.buttons.forEach((buttonDesc, index) => { this.buttons.forEach((buttonDesc, index) => {
const buttonElem = this.element.querySelector(`#button-${index}`) as HTMLButtonElement const buttonElem = this.element.querySelector(`#button-${index}`) as HTMLButtonElement
buttonElem.onclick = () => { buttonElem.onclick = () => {
buttonDesc.action && buttonDesc.action() if (buttonDesc.action) {
buttonDesc.action()
}
this.dismiss() this.dismiss()
} }
if (index === 0) { if (index === 0) {

View File

@@ -73,6 +73,7 @@ export const IconNameToSvgMapping = {
archive: icons.ArchiveIcon, archive: icons.ArchiveIcon,
asterisk: icons.AsteriskIcon, asterisk: icons.AsteriskIcon,
authenticator: icons.AuthenticatorIcon, authenticator: icons.AuthenticatorIcon,
backspace: icons.BackspaceIcon,
bold: icons.BoldIcon, bold: icons.BoldIcon,
camera: icons.CameraIcon, camera: icons.CameraIcon,
check: icons.CheckIcon, check: icons.CheckIcon,

View File

@@ -4,7 +4,7 @@ import useModal from '../../Lexical/Hooks/useModal'
import { InsertTableDialog } from '../../Plugins/TablePlugin' import { InsertTableDialog } from '../../Plugins/TablePlugin'
import { getSelectedNode } from '../../Lexical/Utils/getSelectedNode' import { getSelectedNode } from '../../Lexical/Utils/getSelectedNode'
import { sanitizeUrl } from '../../Lexical/Utils/sanitizeUrl' 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 { $isLinkNode, TOGGLE_LINK_COMMAND } from '@lexical/link'
import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { GetAlignmentBlocks } from '../Blocks/Alignment' import { GetAlignmentBlocks } from '../Blocks/Alignment'
@@ -23,11 +23,14 @@ import { GetPasswordBlock } from '../Blocks/Password'
import { GetQuoteBlock } from '../Blocks/Quote' import { GetQuoteBlock } from '../Blocks/Quote'
import { GetTableBlock } from '../Blocks/Table' import { GetTableBlock } from '../Blocks/Table'
import { MutuallyExclusiveMediaQueryBreakpoints, useMediaQuery } from '@/Hooks/useMediaQuery' 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 { SUPER_TOGGLE_SEARCH } from '@standardnotes/ui-services'
import { useApplication } from '@/Components/ApplicationProvider' import { useApplication } from '@/Components/ApplicationProvider'
import { GetRemoteImageBlock } from '../Blocks/RemoteImage' import { GetRemoteImageBlock } from '../Blocks/RemoteImage'
import { InsertRemoteImageDialog } from '../RemoteImagePlugin/RemoteImagePlugin' import { InsertRemoteImageDialog } from '../RemoteImagePlugin/RemoteImagePlugin'
import { SKAlert } from '@standardnotes/styles'
const DontShowSuperAndroidBackspaceAlertKey = 'dontShowSuperAndroidBackspaceAlert'
const MobileToolbarPlugin = () => { const MobileToolbarPlugin = () => {
const application = useApplication() const application = useApplication()
@@ -37,8 +40,10 @@ const MobileToolbarPlugin = () => {
const [isInEditor, setIsInEditor] = useState(false) const [isInEditor, setIsInEditor] = useState(false)
const [isInToolbar, setIsInToolbar] = useState(false) const [isInToolbar, setIsInToolbar] = useState(false)
const isMobile = useMediaQuery(MutuallyExclusiveMediaQueryBreakpoints.sm) const isMobile = useMediaQuery(MutuallyExclusiveMediaQueryBreakpoints.sm)
const isAndroid = application.platform === Platform.Android
const toolbarRef = useRef<HTMLDivElement>(null) const toolbarRef = useRef<HTMLDivElement>(null)
const backspaceButtonRef = useRef<HTMLButtonElement>(null)
const insertLink = useCallback(() => { const insertLink = useCallback(() => {
const selection = $getSelection() const selection = $getSelection()
@@ -156,7 +161,8 @@ const MobileToolbarPlugin = () => {
const handleFocus = () => setIsInEditor(true) const handleFocus = () => setIsInEditor(true)
const handleBlur = (event: FocusEvent) => { 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 return
} }
setIsInEditor(false) setIsInEditor(false)
@@ -179,7 +185,13 @@ const MobileToolbarPlugin = () => {
const toolbar = toolbarRef.current const toolbar = toolbarRef.current
const handleFocus = () => setIsInToolbar(true) 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('focus', handleFocus)
toolbar.addEventListener('blur', handleBlur) 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 const isFocusInEditorOrToolbar = isInEditor || isInToolbar
if (!isMobile || !isFocusInEditorOrToolbar) { if (!isMobile || !isFocusInEditorOrToolbar) {
return null return null
@@ -223,6 +267,20 @@ const MobileToolbarPlugin = () => {
> >
<Icon type="keyboard-close" size="medium" /> <Icon type="keyboard-close" size="medium" />
</button> </button>
{isAndroid && (
<button
className="flex flex-shrink-0 items-center justify-center rounded border-l border-border py-3 px-3"
aria-label="Backspace"
ref={backspaceButtonRef}
onClick={() => {
editor.update(() => {
editor.dispatchCommand(DELETE_CHARACTER_COMMAND, true)
})
}}
>
<Icon type="backspace" size="medium" />
</button>
)}
</div> </div>
</> </>
) )