chore: show Super demo modal if user doesn't have subscription when switching editor to Super

This commit is contained in:
Aman Harwara
2023-12-22 16:40:42 +05:30
committed by GitHub
parent 485339be86
commit 29b7e989a6
26 changed files with 482 additions and 119 deletions

View File

@@ -28,6 +28,13 @@ import TableActionMenuPlugin from './Plugins/TableCellActionMenuPlugin'
import ToolbarPlugin from './Plugins/ToolbarPlugin/ToolbarPlugin'
import { useMediaQuery, MutuallyExclusiveMediaQueryBreakpoints } from '@/Hooks/useMediaQuery'
import { CheckListPlugin } from './Plugins/List/CheckListPlugin'
import RemoteImagePlugin from './Plugins/RemoteImagePlugin/RemoteImagePlugin'
import CodeOptionsPlugin from './Plugins/CodeOptionsPlugin/CodeOptions'
import { SuperSearchContextProvider } from './Plugins/SearchPlugin/Context'
import { SearchPlugin } from './Plugins/SearchPlugin/SearchPlugin'
import AutoLinkPlugin from './Plugins/AutoLinkPlugin/AutoLinkPlugin'
import DatetimePlugin from './Plugins/DateTimePlugin/DateTimePlugin'
import PasswordPlugin from './Plugins/PasswordPlugin/PasswordPlugin'
type BlocksEditorProps = {
onChange?: (value: string, preview: string) => void
@@ -83,7 +90,10 @@ export const BlocksEditor: FunctionComponent<BlocksEditorProps> = ({
<div className="editor z-0 overflow-hidden" ref={onRef}>
<ContentEditable
id={SuperEditorContentId}
className={classNames('ContentEditable__root overflow-y-auto', className)}
className={classNames(
'ContentEditable__root relative overflow-y-auto p-4 text-[length:--font-size] leading-[--line-height] focus:shadow-none focus:outline-none',
className,
)}
spellCheck={spellcheck}
/>
<div className="search-highlight-container pointer-events-none absolute left-0 top-0 h-full w-full" />
@@ -116,6 +126,14 @@ export const BlocksEditor: FunctionComponent<BlocksEditorProps> = ({
<CollapsiblePlugin />
<TabIndentationPlugin />
<RemoveBrokenTablesPlugin />
<RemoteImagePlugin />
<CodeOptionsPlugin />
<SuperSearchContextProvider>
<SearchPlugin />
</SuperSearchContextProvider>
<DatetimePlugin />
<PasswordPlugin />
<AutoLinkPlugin />
{!readonly && floatingAnchorElem && (
<>
<DraggableBlockPlugin anchorElem={floatingAnchorElem} />

View File

@@ -1,11 +1,11 @@
import { FunctionComponent } from 'react'
import { LexicalComposer } from '@lexical/react/LexicalComposer'
import { LexicalComposer, InitialEditorStateType } from '@lexical/react/LexicalComposer'
import BlocksEditorTheme from './Lexical/Theme/Theme'
import { BlockEditorNodes } from './Lexical/Nodes/AllNodes'
import { Klass, LexicalNode } from 'lexical'
type BlocksEditorComposerProps = {
initialValue: string | undefined
initialValue: InitialEditorStateType | undefined
children: React.ReactNode
nodes?: Array<Klass<LexicalNode>>
readonly?: boolean
@@ -24,7 +24,7 @@ export const BlocksEditorComposer: FunctionComponent<BlocksEditorComposerProps>
theme: BlocksEditorTheme,
editable: !readonly,
onError: (error: Error) => console.error(error),
editorState: initialValue && initialValue.length > 0 ? initialValue : undefined,
editorState: initialValue,
nodes: [...nodes, ...BlockEditorNodes],
}}
>

View File

@@ -11,7 +11,7 @@
display: flex;
justify-content: center;
align-items: center;
position: fixed;
position: absolute;
flex-direction: column;
top: 0px;
bottom: 0px;

View File

@@ -6,9 +6,10 @@
*
*/
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'
import './Modal.css'
import { ReactNode, useEffect, useRef } from 'react'
import { ReactNode, useEffect, useRef, useState } from 'react'
import { createPortal } from 'react-dom'
function PortalImpl({
@@ -83,11 +84,22 @@ export default function Modal({
closeOnClickOutside?: boolean
onClose: () => void
title: string
}): JSX.Element {
}): ReactNode {
const [containerElement, setContainerElement] = useState<HTMLElement | undefined>()
const [editor] = useLexicalComposerContext()
useEffect(() => {
setContainerElement(editor.getRootElement()?.parentElement ?? document.body)
}, [editor])
if (!containerElement) {
return null
}
return createPortal(
<PortalImpl onClose={onClose} title={title} closeOnClickOutside={closeOnClickOutside}>
{children}
</PortalImpl>,
document.body,
containerElement,
)
}

View File

@@ -19,7 +19,7 @@ export function BlockPickerMenuItem({
<li
key={option.key}
tabIndex={-1}
className={`border-bottom gap-3 border-[0.5px] border-border ${PopoverItemClassNames} ${
className={`gap-3 border-b-[0.5px] border-border ${PopoverItemClassNames} ${
isSelected ? PopoverItemSelectedClassNames : ''
}`}
ref={option.setRefElement}

View File

@@ -30,7 +30,7 @@ import { GetDividerBlockOption } from '../Blocks/Divider'
import { GetCollapsibleBlockOption } from '../Blocks/Collapsible'
import { GetEmbedsBlockOptions } from '../Blocks/Embeds'
export default function BlockPickerMenuPlugin(): JSX.Element {
export default function BlockPickerMenuPlugin({ popoverZIndex }: { popoverZIndex?: string }): JSX.Element {
const [editor] = useLexicalComposerContext()
const application = useApplication()
const [modal, showModal] = useModal()
@@ -130,6 +130,7 @@ export default function BlockPickerMenuPlugin(): JSX.Element {
disableMobileFullscreenTakeover={true}
side={isMobileScreen() ? 'top' : 'bottom'}
maxHeight={(mh) => mh / 2}
overrideZIndex={popoverZIndex}
>
<ul>
{options.map((option, i: number) => (

View File

@@ -113,8 +113,19 @@ const ToolbarButton = forwardRef(
) => {
const [editor] = useLexicalComposerContext()
const isMobile = useMediaQuery(MutuallyExclusiveMediaQueryBreakpoints.sm)
const parentElement = editor.getRootElement()?.parentElement ?? document.body
return (
<StyledTooltip showOnMobile showOnHover label={name} side="top">
<StyledTooltip
showOnMobile
showOnHover
label={name}
side="top"
portal={false}
portalElement={isMobile ? parentElement : undefined}
documentElement={parentElement}
>
<ToolbarItem
className={classNames(
'flex select-none items-center justify-center rounded p-0.5 focus:shadow-none focus:outline-none enabled:hover:bg-default enabled:focus-visible:bg-default disabled:opacity-50 md:border md:border-transparent enabled:hover:md:translucent-ui:border-[--popover-border-color]',
@@ -572,6 +583,8 @@ const ToolbarPlugin = () => {
toolbarStore,
])
const popoverDocumentElement = editor.getRootElement()?.parentElement ?? document.body
return (
<>
{modal}
@@ -746,6 +759,8 @@ const ToolbarPlugin = () => {
className="py-1"
disableMobileFullscreenTakeover
disableFlip
portal={false}
documentElement={isMobile ? popoverDocumentElement : undefined}
>
<div className="mb-1.5 mt-1 px-3 text-sm font-semibold uppercase text-text">Table of Contents</div>
<LexicalTableOfContents>
@@ -800,6 +815,8 @@ const ToolbarPlugin = () => {
disableMobileFullscreenTakeover
disableFlip
containerClassName="md:!min-w-60 md:!w-auto"
portal={false}
documentElement={isMobile ? popoverDocumentElement : undefined}
>
<Menu a11yLabel="Text formatting options" className="!px-0" onClick={() => setIsTextFormatMenuOpen(false)}>
<ToolbarMenuItem
@@ -839,6 +856,8 @@ const ToolbarPlugin = () => {
disableMobileFullscreenTakeover
disableFlip
containerClassName="md:!min-w-60 md:!w-auto"
portal={false}
documentElement={isMobile ? popoverDocumentElement : undefined}
>
<Menu a11yLabel="Text style" className="!px-0" onClick={() => setIsTextStyleMenuOpen(false)}>
<ToolbarMenuItem
@@ -910,6 +929,8 @@ const ToolbarPlugin = () => {
disableMobileFullscreenTakeover
disableFlip
containerClassName="md:!min-w-60 md:!w-auto"
portal={false}
documentElement={isMobile ? popoverDocumentElement : undefined}
>
<Menu a11yLabel="Alignment" className="!px-0" onClick={() => setIsAlignmentMenuOpen(false)}>
<ToolbarMenuItem
@@ -949,6 +970,8 @@ const ToolbarPlugin = () => {
disableMobileFullscreenTakeover
disableFlip
containerClassName="md:!min-w-60 md:!w-auto"
portal={false}
documentElement={isMobile ? popoverDocumentElement : undefined}
>
<Menu a11yLabel="Insert" className="!px-0" onClick={() => setIsInsertMenuOpen(false)}>
<ToolbarMenuItem

View File

@@ -1,6 +1,5 @@
import { WebApplication } from '@/Application/WebApplication'
import {
classNames,
isPayloadSourceRetrieved,
PrefKey,
NativeFeatureIdentifier,
@@ -14,7 +13,6 @@ import { BlocksEditorComposer } from './BlocksEditorComposer'
import { ItemSelectionPlugin } from './Plugins/ItemSelectionPlugin/ItemSelectionPlugin'
import { FileNode } from './Plugins/EncryptedFilePlugin/Nodes/FileNode'
import FilePlugin from './Plugins/EncryptedFilePlugin/FilePlugin'
import BlockPickerMenuPlugin from './Plugins/BlockPickerPlugin/BlockPickerPlugin'
import { ErrorBoundary } from '@/Utils/ErrorBoundary'
import { LinkingController } from '@/Controllers/LinkingController'
import LinkingControllerProvider from '../../Controllers/LinkingControllerProvider'
@@ -23,28 +21,22 @@ import ItemBubblePlugin from './Plugins/ItemBubblePlugin/ItemBubblePlugin'
import { NodeObserverPlugin } from './Plugins/NodeObserverPlugin/NodeObserverPlugin'
import { FilesController } from '@/Controllers/FilesController'
import FilesControllerProvider from '@/Controllers/FilesControllerProvider'
import DatetimePlugin from './Plugins/DateTimePlugin/DateTimePlugin'
import AutoLinkPlugin from './Plugins/AutoLinkPlugin/AutoLinkPlugin'
import { NoteViewController } from '../NoteView/Controller/NoteViewController'
import {
ChangeContentCallbackPlugin,
ChangeEditorFunction,
} from './Plugins/ChangeContentCallback/ChangeContentCallback'
import PasswordPlugin from './Plugins/PasswordPlugin/PasswordPlugin'
import { useCommandService } from '@/Components/CommandProvider'
import { SUPER_SHOW_MARKDOWN_PREVIEW } from '@standardnotes/ui-services'
import { SuperNoteMarkdownPreview } from './SuperNoteMarkdownPreview'
import GetMarkdownPlugin, { GetMarkdownPluginInterface } from './Plugins/GetMarkdownPlugin/GetMarkdownPlugin'
import { useResponsiveEditorFontSize } from '@/Utils/getPlaintextFontSize'
import ReadonlyPlugin from './Plugins/ReadonlyPlugin/ReadonlyPlugin'
import { SuperSearchContextProvider } from './Plugins/SearchPlugin/Context'
import { SearchPlugin } from './Plugins/SearchPlugin/SearchPlugin'
import ModalOverlay from '@/Components/Modal/ModalOverlay'
import CodeOptionsPlugin from './Plugins/CodeOptionsPlugin/CodeOptions'
import RemoteImagePlugin from './Plugins/RemoteImagePlugin/RemoteImagePlugin'
import NotEntitledBanner from '../ComponentView/NotEntitledBanner'
import AutoFocusPlugin from './Plugins/AutoFocusPlugin'
import usePreference from '@/Hooks/usePreference'
import BlockPickerMenuPlugin from './Plugins/BlockPickerPlugin/BlockPickerPlugin'
export const SuperNotePreviewCharLimit = 160
@@ -207,10 +199,7 @@ export const SuperEditor: FunctionComponent<Props> = ({
<BlocksEditorComposer readonly={note.current.locked || readonly} initialValue={note.current.text}>
<BlocksEditor
onChange={handleChange}
className={classNames(
'blocks-editor relative h-full resize-none px-4 py-4 text-[length:--font-size] focus:shadow-none focus:outline-none',
lineHeight && 'leading-[--line-height]',
)}
className="blocks-editor h-full resize-none"
previewLength={SuperNotePreviewCharLimit}
spellcheck={spellcheck}
readonly={note.current.locked || readonly}
@@ -218,11 +207,7 @@ export const SuperEditor: FunctionComponent<Props> = ({
<ItemSelectionPlugin currentNote={note.current} />
<FilePlugin currentNote={note.current} />
<ItemBubblePlugin />
<BlockPickerMenuPlugin />
<GetMarkdownPlugin ref={getMarkdownPlugin} />
<DatetimePlugin />
<PasswordPlugin />
<AutoLinkPlugin />
<ChangeContentCallbackPlugin
providerCallback={(callback) => (changeEditorFunction.current = callback)}
/>
@@ -230,11 +215,7 @@ export const SuperEditor: FunctionComponent<Props> = ({
<NodeObserverPlugin nodeType={FileNode} onRemove={handleBubbleRemove} />
{readonly === undefined && <ReadonlyPlugin note={note.current} />}
<AutoFocusPlugin isEnabled={controller.isTemplateNote} />
<SuperSearchContextProvider>
<SearchPlugin />
</SuperSearchContextProvider>
<CodeOptionsPlugin />
<RemoteImagePlugin />
<BlockPickerMenuPlugin />
</BlocksEditor>
</BlocksEditorComposer>
</FilesControllerProvider>

View File

@@ -155,7 +155,11 @@ export class HeadlessSuperConverter implements SuperConverterServiceInterface {
return content
}
convertOtherFormatToSuperString(otherFormatString: string, fromFormat: 'txt' | 'md' | 'html' | 'json'): string {
convertOtherFormatToSuperString: SuperConverterServiceInterface['convertOtherFormatToSuperString'] = (
otherFormatString,
fromFormat,
options,
) => {
if (otherFormatString.length === 0) {
return otherFormatString
}
@@ -175,6 +179,10 @@ export class HeadlessSuperConverter implements SuperConverterServiceInterface {
let didThrow = false
if (fromFormat === 'html') {
const htmlOptions = options?.html || {
addLineBreaks: true,
}
this.importEditor.update(
() => {
try {
@@ -203,7 +211,9 @@ export class HeadlessSuperConverter implements SuperConverterServiceInterface {
nodesToInsert.push(node)
}
nodesToInsert.push($createParagraphNode())
if (htmlOptions.addLineBreaks) {
nodesToInsert.push($createParagraphNode())
}
})
$getRoot().selectEnd()
$insertNodes(nodesToInsert.concat($createParagraphNode()))