refactor: block storage (#1952)
This commit is contained in:
@@ -5,21 +5,11 @@ 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,
|
||||
NoteType,
|
||||
PrefKey,
|
||||
SNComponent,
|
||||
SNNote,
|
||||
TransactionalMutation,
|
||||
} from '@standardnotes/snjs'
|
||||
import { ComponentArea, NoteMutator, PrefKey, SNComponent, SNNote } from '@standardnotes/snjs'
|
||||
import { Fragment, FunctionComponent, useCallback, useEffect, useMemo, 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 { createEditorMenuGroups } from '../../Utils/createEditorMenuGroups'
|
||||
import { reloadFont } from '../NoteView/FontFunctions'
|
||||
import { PremiumFeatureIconClass, PremiumFeatureIconName } from '../Icon/PremiumFeatureIcon'
|
||||
|
||||
@@ -48,107 +38,96 @@ const ChangeEditorMenu: FunctionComponent<ChangeEditorMenuProps> = ({
|
||||
[application.componentManager],
|
||||
)
|
||||
const groups = useMemo(() => createEditorMenuGroups(application, editors), [application, editors])
|
||||
const [currentEditor, setCurrentEditor] = useState<SNComponent>()
|
||||
const [currentComponent, setCurrentComponent] = useState<SNComponent>()
|
||||
|
||||
useEffect(() => {
|
||||
if (note) {
|
||||
setCurrentEditor(application.componentManager.editorForNote(note))
|
||||
setCurrentComponent(application.componentManager.editorForNote(note))
|
||||
}
|
||||
}, [application, note])
|
||||
|
||||
const premiumModal = usePremiumModal()
|
||||
|
||||
const isSelectedEditor = useCallback(
|
||||
const isSelected = useCallback(
|
||||
(item: EditorMenuItem) => {
|
||||
if (currentEditor) {
|
||||
if (item?.component?.identifier === currentEditor.identifier) {
|
||||
return true
|
||||
}
|
||||
} else if (item.name === PLAIN_EDITOR_NAME) {
|
||||
return true
|
||||
if (currentComponent) {
|
||||
return item.component?.identifier === currentComponent.identifier
|
||||
}
|
||||
return false
|
||||
|
||||
return item.noteType === note?.noteType
|
||||
},
|
||||
[currentEditor],
|
||||
[currentComponent, note],
|
||||
)
|
||||
|
||||
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)
|
||||
}
|
||||
async (component: SNComponent, note: SNNote) => {
|
||||
if (component.conflictOf) {
|
||||
void application.mutator.changeAndSaveItem(component, (mutator) => {
|
||||
mutator.conflictOf = undefined
|
||||
})
|
||||
}
|
||||
|
||||
const transactions: TransactionalMutation[] = []
|
||||
|
||||
await application.getViewControllerManager().itemListController.insertCurrentIfTemplate()
|
||||
|
||||
if (note.locked) {
|
||||
application.alertService.alert(STRING_EDIT_LOCKED_ATTEMPT).catch(console.error)
|
||||
return
|
||||
}
|
||||
await application.mutator.changeAndSaveItem(note, (mutator) => {
|
||||
const noteMutator = mutator as NoteMutator
|
||||
noteMutator.noteType = component.noteType
|
||||
noteMutator.editorIdentifier = component.identifier
|
||||
})
|
||||
|
||||
if (!component) {
|
||||
transactions.push({
|
||||
itemUuid: note.uuid,
|
||||
mutate: (m: ItemMutator) => {
|
||||
const noteMutator = m as NoteMutator
|
||||
noteMutator.noteType = NoteType.Plain
|
||||
noteMutator.editorIdentifier = undefined
|
||||
},
|
||||
})
|
||||
reloadFont(application.getPreference(PrefKey.EditorMonospaceEnabled))
|
||||
} else {
|
||||
transactions.push({
|
||||
itemUuid: note.uuid,
|
||||
mutate: (m: ItemMutator) => {
|
||||
const noteMutator = m as NoteMutator
|
||||
noteMutator.noteType = component.noteType
|
||||
noteMutator.editorIdentifier = component.identifier
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
await application.mutator.runTransactionalMutations(transactions)
|
||||
application.sync.sync().catch(console.error)
|
||||
setCurrentEditor(application.componentManager.editorForNote(note))
|
||||
setCurrentComponent(application.componentManager.editorForNote(note))
|
||||
},
|
||||
[application],
|
||||
)
|
||||
|
||||
const selectEditor = useCallback(
|
||||
const selectNonComponent = useCallback(
|
||||
async (item: EditorMenuItem, note: SNNote) => {
|
||||
await application.getViewControllerManager().itemListController.insertCurrentIfTemplate()
|
||||
|
||||
reloadFont(application.getPreference(PrefKey.EditorMonospaceEnabled))
|
||||
|
||||
await application.mutator.changeAndSaveItem(note, (mutator) => {
|
||||
const noteMutator = mutator as NoteMutator
|
||||
noteMutator.noteType = item.noteType
|
||||
noteMutator.editorIdentifier = undefined
|
||||
})
|
||||
|
||||
setCurrentComponent(undefined)
|
||||
},
|
||||
[application],
|
||||
)
|
||||
|
||||
const selectItem = useCallback(
|
||||
async (itemToBeSelected: EditorMenuItem) => {
|
||||
if (!itemToBeSelected.isEntitled) {
|
||||
premiumModal.activate(itemToBeSelected.name)
|
||||
return
|
||||
}
|
||||
|
||||
const areBothEditorsPlain = !currentEditor && !itemToBeSelected.component
|
||||
|
||||
if (areBothEditorsPlain) {
|
||||
if (note?.locked) {
|
||||
application.alertService.alert(STRING_EDIT_LOCKED_ATTEMPT).catch(console.error)
|
||||
return
|
||||
}
|
||||
|
||||
let shouldSelectEditor = true
|
||||
let shouldMakeSelection = true
|
||||
|
||||
if (itemToBeSelected.component) {
|
||||
const changeRequiresAlert = application.componentManager.doesEditorChangeRequireAlert(
|
||||
currentEditor,
|
||||
currentComponent,
|
||||
itemToBeSelected.component,
|
||||
)
|
||||
|
||||
if (changeRequiresAlert) {
|
||||
shouldSelectEditor = await application.componentManager.showEditorChangeAlert()
|
||||
shouldMakeSelection = await application.componentManager.showEditorChangeAlert()
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldSelectEditor && note) {
|
||||
selectComponent(itemToBeSelected.component ?? null, note).catch(console.error)
|
||||
if (shouldMakeSelection && note) {
|
||||
if (itemToBeSelected.component) {
|
||||
selectComponent(itemToBeSelected.component, note).catch(console.error)
|
||||
} else {
|
||||
selectNonComponent(itemToBeSelected, note).catch(console.error)
|
||||
}
|
||||
}
|
||||
|
||||
closeMenu()
|
||||
@@ -157,7 +136,7 @@ const ChangeEditorMenu: FunctionComponent<ChangeEditorMenuProps> = ({
|
||||
onSelect(itemToBeSelected.component)
|
||||
}
|
||||
},
|
||||
[application.componentManager, closeMenu, currentEditor, note, onSelect, premiumModal, selectComponent],
|
||||
[application, closeMenu, currentComponent, note, onSelect, premiumModal, selectComponent, selectNonComponent],
|
||||
)
|
||||
|
||||
return (
|
||||
@@ -172,7 +151,7 @@ const ChangeEditorMenu: FunctionComponent<ChangeEditorMenuProps> = ({
|
||||
<div className={`border-0 border-t border-solid border-border py-1 ${index === 0 ? 'border-t-0' : ''}`}>
|
||||
{group.items.map((item) => {
|
||||
const onClickEditorItem = () => {
|
||||
selectEditor(item).catch(console.error)
|
||||
selectItem(item).catch(console.error)
|
||||
}
|
||||
return (
|
||||
<MenuItem
|
||||
@@ -180,7 +159,7 @@ const ChangeEditorMenu: FunctionComponent<ChangeEditorMenuProps> = ({
|
||||
type={MenuItemType.RadioButton}
|
||||
onClick={onClickEditorItem}
|
||||
className={'flex-row-reverse py-2'}
|
||||
checked={item.isEntitled ? isSelectedEditor(item) : undefined}
|
||||
checked={item.isEntitled ? isSelected(item) : undefined}
|
||||
>
|
||||
<div className="flex flex-grow items-center justify-between">
|
||||
<div className="flex items-center">
|
||||
|
||||
@@ -1,128 +0,0 @@
|
||||
import { WebApplication } from '@/Application/Application'
|
||||
import {
|
||||
ContentType,
|
||||
FeatureStatus,
|
||||
SNComponent,
|
||||
ComponentArea,
|
||||
FeatureDescription,
|
||||
GetFeatures,
|
||||
NoteType,
|
||||
} from '@standardnotes/snjs'
|
||||
import { EditorMenuGroup } from '@/Components/NotesOptions/EditorMenuGroup'
|
||||
import { EditorMenuItem } from '@/Components/NotesOptions/EditorMenuItem'
|
||||
import { PLAIN_EDITOR_NAME } from '@/Constants/Constants'
|
||||
|
||||
type EditorGroup = NoteType | 'others'
|
||||
|
||||
const getEditorGroup = (featureDescription: FeatureDescription): EditorGroup => {
|
||||
if (featureDescription.note_type) {
|
||||
return featureDescription.note_type
|
||||
} else if (featureDescription.file_type) {
|
||||
switch (featureDescription.file_type) {
|
||||
case 'html':
|
||||
return NoteType.RichText
|
||||
case 'md':
|
||||
return NoteType.Markdown
|
||||
default:
|
||||
return 'others'
|
||||
}
|
||||
}
|
||||
return 'others'
|
||||
}
|
||||
|
||||
export const createEditorMenuGroups = (application: WebApplication, editors: SNComponent[]) => {
|
||||
const editorItems: Record<EditorGroup, EditorMenuItem[]> = {
|
||||
'plain-text': [
|
||||
{
|
||||
name: PLAIN_EDITOR_NAME,
|
||||
isEntitled: true,
|
||||
},
|
||||
],
|
||||
'rich-text': [],
|
||||
markdown: [],
|
||||
task: [],
|
||||
code: [],
|
||||
spreadsheet: [],
|
||||
authentication: [],
|
||||
others: [],
|
||||
blocks: [],
|
||||
}
|
||||
|
||||
GetFeatures()
|
||||
.filter((feature) => feature.content_type === ContentType.Component && feature.area === ComponentArea.Editor)
|
||||
.forEach((editorFeature) => {
|
||||
const notInstalled = !editors.find((editor) => editor.identifier === editorFeature.identifier)
|
||||
const isExperimental = application.features.isExperimentalFeature(editorFeature.identifier)
|
||||
const isDeprecated = editorFeature.deprecated
|
||||
const isShowable = notInstalled && !isExperimental && !isDeprecated
|
||||
if (isShowable) {
|
||||
editorItems[getEditorGroup(editorFeature)].push({
|
||||
name: editorFeature.name as string,
|
||||
isEntitled: false,
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
editors.forEach((editor) => {
|
||||
const editorItem: EditorMenuItem = {
|
||||
name: editor.displayName,
|
||||
component: editor,
|
||||
isEntitled: application.features.getFeatureStatus(editor.identifier) === FeatureStatus.Entitled,
|
||||
}
|
||||
|
||||
editorItems[getEditorGroup(editor.package_info)].push(editorItem)
|
||||
})
|
||||
|
||||
const editorMenuGroups: EditorMenuGroup[] = [
|
||||
{
|
||||
icon: 'plain-text',
|
||||
iconClassName: 'text-accessory-tint-1',
|
||||
title: 'Plain text',
|
||||
items: editorItems['plain-text'],
|
||||
},
|
||||
{
|
||||
icon: 'rich-text',
|
||||
iconClassName: 'text-accessory-tint-1',
|
||||
title: 'Rich text',
|
||||
items: editorItems['rich-text'],
|
||||
},
|
||||
{
|
||||
icon: 'markdown',
|
||||
iconClassName: 'text-accessory-tint-2',
|
||||
title: 'Markdown text',
|
||||
items: editorItems.markdown,
|
||||
},
|
||||
{
|
||||
icon: 'tasks',
|
||||
iconClassName: 'text-accessory-tint-3',
|
||||
title: 'Todo',
|
||||
items: editorItems.task,
|
||||
},
|
||||
{
|
||||
icon: 'code',
|
||||
iconClassName: 'text-accessory-tint-4',
|
||||
title: 'Code',
|
||||
items: editorItems.code,
|
||||
},
|
||||
{
|
||||
icon: 'spreadsheets',
|
||||
iconClassName: 'text-accessory-tint-5',
|
||||
title: 'Spreadsheet',
|
||||
items: editorItems.spreadsheet,
|
||||
},
|
||||
{
|
||||
icon: 'authenticator',
|
||||
iconClassName: 'text-accessory-tint-6',
|
||||
title: 'Authentication',
|
||||
items: editorItems.authentication,
|
||||
},
|
||||
{
|
||||
icon: 'editor',
|
||||
iconClassName: 'text-neutral',
|
||||
title: 'Others',
|
||||
items: editorItems.others,
|
||||
},
|
||||
]
|
||||
|
||||
return editorMenuGroups
|
||||
}
|
||||
Reference in New Issue
Block a user