import Icon from '@/Components/Icon/Icon' import Menu from '@/Components/Menu/Menu' import MenuItem from '@/Components/Menu/MenuItem' import { MenuItemType } from '@/Components/Menu/MenuItemType' import { usePremiumModal } from '@/Hooks/usePremiumModal' import { STRING_EDIT_LOCKED_ATTEMPT } from '@/Constants/Strings' import { WebApplication } from '@/Application/Application' import { ComponentArea, ItemMutator, NoteMutator, PrefKey, SNComponent, SNNote, TransactionalMutation, } from '@standardnotes/snjs' import { Fragment, FunctionComponent, useCallback, useEffect, useState } from 'react' import { EditorMenuGroup } from '@/Components/NotesOptions/EditorMenuGroup' import { EditorMenuItem } from '@/Components/NotesOptions/EditorMenuItem' import { createEditorMenuGroups } from './createEditorMenuGroups' import { PLAIN_EDITOR_NAME } from '@/Constants/Constants' import { transactionForAssociateComponentWithCurrentNote, transactionForDisassociateComponentWithCurrentNote, } from '../NoteView/TransactionFunctions' import { reloadFont } from '../NoteView/FontFunctions' type ChangeEditorMenuProps = { application: WebApplication closeOnBlur: (event: { relatedTarget: EventTarget | null }) => void closeMenu: () => void isVisible: boolean note: SNNote | undefined } const getGroupId = (group: EditorMenuGroup) => group.title.toLowerCase().replace(/\s/, '-') const ChangeEditorMenu: FunctionComponent = ({ application, closeOnBlur, closeMenu, isVisible, note, }) => { const [editors] = useState(() => application.componentManager.componentsForArea(ComponentArea.Editor).sort((a, b) => { return a.displayName.toLowerCase() < b.displayName.toLowerCase() ? -1 : 1 }), ) const [groups, setGroups] = useState([]) const [currentEditor, setCurrentEditor] = useState() useEffect(() => { setGroups(createEditorMenuGroups(application, editors)) }, [application, editors]) useEffect(() => { if (note) { setCurrentEditor(application.componentManager.editorForNote(note)) } }, [application, note]) const premiumModal = usePremiumModal() const isSelectedEditor = useCallback( (item: EditorMenuItem) => { if (currentEditor) { if (item?.component?.identifier === currentEditor.identifier) { return true } } else if (item.name === PLAIN_EDITOR_NAME) { return true } return false }, [currentEditor], ) const selectComponent = useCallback( async (component: SNComponent | null, note: SNNote) => { if (component) { if (component.conflictOf) { application.mutator .changeAndSaveItem(component, (mutator) => { mutator.conflictOf = undefined }) .catch(console.error) } } const transactions: TransactionalMutation[] = [] await application.getViewControllerManager().contentListController.insertCurrentIfTemplate() if (note.locked) { application.alertService.alert(STRING_EDIT_LOCKED_ATTEMPT).catch(console.error) return } if (!component) { if (!note.prefersPlainEditor) { transactions.push({ itemUuid: note.uuid, mutate: (m: ItemMutator) => { const noteMutator = m as NoteMutator noteMutator.prefersPlainEditor = true }, }) } const currentEditor = application.componentManager.editorForNote(note) if (currentEditor?.isExplicitlyEnabledForItem(note.uuid)) { transactions.push(transactionForDisassociateComponentWithCurrentNote(currentEditor, note)) } reloadFont(application.getPreference(PrefKey.EditorMonospaceEnabled)) } else if (component.area === ComponentArea.Editor) { const currentEditor = application.componentManager.editorForNote(note) if (currentEditor && component.uuid !== currentEditor.uuid) { transactions.push(transactionForDisassociateComponentWithCurrentNote(currentEditor, note)) } const prefersPlain = note.prefersPlainEditor if (prefersPlain) { transactions.push({ itemUuid: note.uuid, mutate: (m: ItemMutator) => { const noteMutator = m as NoteMutator noteMutator.prefersPlainEditor = false }, }) } transactions.push(transactionForAssociateComponentWithCurrentNote(component, note)) } await application.mutator.runTransactionalMutations(transactions) /** Dirtying can happen above */ application.sync.sync().catch(console.error) setCurrentEditor(application.componentManager.editorForNote(note)) }, [application], ) const selectEditor = useCallback( async (itemToBeSelected: EditorMenuItem) => { if (!itemToBeSelected.isEntitled) { premiumModal.activate(itemToBeSelected.name) return } const areBothEditorsPlain = !currentEditor && !itemToBeSelected.component if (areBothEditorsPlain) { return } let shouldSelectEditor = true if (itemToBeSelected.component) { const changeRequiresAlert = application.componentManager.doesEditorChangeRequireAlert( currentEditor, itemToBeSelected.component, ) if (changeRequiresAlert) { shouldSelectEditor = await application.componentManager.showEditorChangeAlert() } } if (shouldSelectEditor && note) { selectComponent(itemToBeSelected.component ?? null, note).catch(console.error) } closeMenu() }, [application.componentManager, closeMenu, currentEditor, note, premiumModal, selectComponent], ) return ( {groups .filter((group) => group.items && group.items.length) .map((group, index) => { const groupId = getGroupId(group) return (
{group.items.map((item) => { const onClickEditorItem = () => { selectEditor(item).catch(console.error) } return (
{group.icon && } {item.name}
{!item.isEntitled && }
) })}
) })}
) } export default ChangeEditorMenu