refactor(web): dependency management (#2386)
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { FileItem } from '@standardnotes/models'
|
||||
import { ContentType } from '@standardnotes/domain-core'
|
||||
import { SNApplication } from '@standardnotes/snjs'
|
||||
import { ItemViewControllerInterface } from './ItemViewControllerInterface'
|
||||
import { ItemManagerInterface } from '@standardnotes/snjs'
|
||||
|
||||
export class FileViewController implements ItemViewControllerInterface {
|
||||
public dealloced = false
|
||||
@@ -9,15 +9,14 @@ export class FileViewController implements ItemViewControllerInterface {
|
||||
public runtimeId = `${Math.random()}`
|
||||
|
||||
constructor(
|
||||
private application: SNApplication,
|
||||
public item: FileItem,
|
||||
private items: ItemManagerInterface,
|
||||
) {}
|
||||
|
||||
deinit() {
|
||||
this.dealloced = true
|
||||
this.removeStreamObserver?.()
|
||||
;(this.removeStreamObserver as unknown) = undefined
|
||||
;(this.application as unknown) = undefined
|
||||
;(this.item as unknown) = undefined
|
||||
}
|
||||
|
||||
@@ -26,23 +25,20 @@ export class FileViewController implements ItemViewControllerInterface {
|
||||
}
|
||||
|
||||
private streamItems() {
|
||||
this.removeStreamObserver = this.application.streamItems<FileItem>(
|
||||
ContentType.TYPES.File,
|
||||
({ changed, inserted }) => {
|
||||
if (this.dealloced) {
|
||||
return
|
||||
}
|
||||
this.removeStreamObserver = this.items.streamItems<FileItem>(ContentType.TYPES.File, ({ changed, inserted }) => {
|
||||
if (this.dealloced) {
|
||||
return
|
||||
}
|
||||
|
||||
const files = changed.concat(inserted)
|
||||
const files = changed.concat(inserted)
|
||||
|
||||
const matchingFile = files.find((item) => {
|
||||
return item.uuid === this.item.uuid
|
||||
})
|
||||
const matchingFile = files.find((item) => {
|
||||
return item.uuid === this.item.uuid
|
||||
})
|
||||
|
||||
if (matchingFile) {
|
||||
this.item = matchingFile
|
||||
}
|
||||
},
|
||||
)
|
||||
if (matchingFile) {
|
||||
this.item = matchingFile
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,19 @@
|
||||
import { WebApplication } from '@/Application/WebApplication'
|
||||
import { removeFromArray } from '@standardnotes/utils'
|
||||
import { FileItem, SNNote } from '@standardnotes/snjs'
|
||||
import {
|
||||
AlertService,
|
||||
ComponentManagerInterface,
|
||||
FileItem,
|
||||
ItemManagerInterface,
|
||||
MutatorClientInterface,
|
||||
PreferenceServiceInterface,
|
||||
SNNote,
|
||||
SessionsClientInterface,
|
||||
SyncServiceInterface,
|
||||
} from '@standardnotes/snjs'
|
||||
import { NoteViewController } from './NoteViewController'
|
||||
import { FileViewController } from './FileViewController'
|
||||
import { TemplateNoteViewControllerOptions } from './TemplateNoteViewControllerOptions'
|
||||
import { IsNativeMobileWeb } from '@standardnotes/ui-services'
|
||||
|
||||
type ItemControllerGroupChangeCallback = (activeController: NoteViewController | FileViewController | undefined) => void
|
||||
|
||||
@@ -12,10 +22,19 @@ export class ItemGroupController {
|
||||
changeObservers: ItemControllerGroupChangeCallback[] = []
|
||||
eventObservers: (() => void)[] = []
|
||||
|
||||
constructor(private application: WebApplication) {}
|
||||
constructor(
|
||||
private items: ItemManagerInterface,
|
||||
private mutator: MutatorClientInterface,
|
||||
private sync: SyncServiceInterface,
|
||||
private sessions: SessionsClientInterface,
|
||||
private preferences: PreferenceServiceInterface,
|
||||
private components: ComponentManagerInterface,
|
||||
private alerts: AlertService,
|
||||
private _isNativeMobileWeb: IsNativeMobileWeb,
|
||||
) {}
|
||||
|
||||
public deinit(): void {
|
||||
;(this.application as unknown) = undefined
|
||||
;(this.items as unknown) = undefined
|
||||
|
||||
this.eventObservers.forEach((removeObserver) => {
|
||||
removeObserver()
|
||||
@@ -42,11 +61,32 @@ export class ItemGroupController {
|
||||
let controller!: NoteViewController | FileViewController
|
||||
|
||||
if (context.file) {
|
||||
controller = new FileViewController(this.application, context.file)
|
||||
controller = new FileViewController(context.file, this.items)
|
||||
} else if (context.note) {
|
||||
controller = new NoteViewController(this.application, context.note)
|
||||
controller = new NoteViewController(
|
||||
context.note,
|
||||
this.items,
|
||||
this.mutator,
|
||||
this.sync,
|
||||
this.sessions,
|
||||
this.preferences,
|
||||
this.components,
|
||||
this.alerts,
|
||||
this._isNativeMobileWeb,
|
||||
)
|
||||
} else if (context.templateOptions) {
|
||||
controller = new NoteViewController(this.application, undefined, context.templateOptions)
|
||||
controller = new NoteViewController(
|
||||
undefined,
|
||||
this.items,
|
||||
this.mutator,
|
||||
this.sync,
|
||||
this.sessions,
|
||||
this.preferences,
|
||||
this.components,
|
||||
this.alerts,
|
||||
this._isNativeMobileWeb,
|
||||
context.templateOptions,
|
||||
)
|
||||
} else {
|
||||
throw Error('Invalid input to createItemController')
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
SyncServiceInterface,
|
||||
ItemManagerInterface,
|
||||
MutatorClientInterface,
|
||||
PreferenceServiceInterface,
|
||||
} from '@standardnotes/snjs'
|
||||
import { NativeFeatureIdentifier, NoteType } from '@standardnotes/features'
|
||||
import { NoteViewController } from './NoteViewController'
|
||||
@@ -18,24 +19,24 @@ describe('note view controller', () => {
|
||||
let componentManager: SNComponentManager
|
||||
|
||||
beforeEach(() => {
|
||||
application = {} as jest.Mocked<WebApplication>
|
||||
application.streamItems = jest.fn().mockReturnValue(() => {})
|
||||
application.getPreference = jest.fn().mockReturnValue(true)
|
||||
application.noAccount = jest.fn().mockReturnValue(false)
|
||||
application.isNativeMobileWeb = jest.fn().mockReturnValue(false)
|
||||
application = {
|
||||
preferences: {
|
||||
getValue: jest.fn().mockReturnValue(true),
|
||||
} as unknown as jest.Mocked<PreferenceServiceInterface>,
|
||||
items: {
|
||||
streamItems: jest.fn().mockReturnValue(() => {}),
|
||||
createTemplateItem: jest.fn().mockReturnValue({} as SNNote),
|
||||
} as unknown as jest.Mocked<ItemManagerInterface>,
|
||||
mutator: {} as jest.Mocked<MutatorClientInterface>,
|
||||
} as unknown as jest.Mocked<WebApplication>
|
||||
|
||||
const items = {} as jest.Mocked<ItemManagerInterface>
|
||||
items.createTemplateItem = jest.fn().mockReturnValue({} as SNNote)
|
||||
Object.defineProperty(application, 'items', { value: items })
|
||||
application.isNativeMobileWeb = jest.fn().mockReturnValue(false)
|
||||
|
||||
Object.defineProperty(application, 'sync', { value: {} as jest.Mocked<SyncServiceInterface> })
|
||||
application.sync.sync = jest.fn().mockReturnValue(Promise.resolve())
|
||||
|
||||
componentManager = {} as jest.Mocked<SNComponentManager>
|
||||
Object.defineProperty(application, 'componentManager', { value: componentManager })
|
||||
|
||||
const mutator = {} as jest.Mocked<MutatorClientInterface>
|
||||
Object.defineProperty(application, 'mutator', { value: mutator })
|
||||
})
|
||||
|
||||
it('should create notes with plaintext note type', async () => {
|
||||
@@ -43,7 +44,17 @@ describe('note view controller', () => {
|
||||
.fn()
|
||||
.mockReturnValue(NativeFeatureIdentifier.TYPES.PlainEditor)
|
||||
|
||||
const controller = new NoteViewController(application)
|
||||
const controller = new NoteViewController(
|
||||
undefined,
|
||||
application.items,
|
||||
application.mutator,
|
||||
application.sync,
|
||||
application.sessions,
|
||||
application.preferences,
|
||||
application.componentManager,
|
||||
application.alerts,
|
||||
application.isNativeMobileWebUseCase,
|
||||
)
|
||||
await controller.initialize()
|
||||
|
||||
expect(application.items.createTemplateItem).toHaveBeenCalledWith(
|
||||
@@ -64,7 +75,17 @@ describe('note view controller', () => {
|
||||
.fn()
|
||||
.mockReturnValue(NativeFeatureIdentifier.TYPES.MarkdownProEditor)
|
||||
|
||||
const controller = new NoteViewController(application)
|
||||
const controller = new NoteViewController(
|
||||
undefined,
|
||||
application.items,
|
||||
application.mutator,
|
||||
application.sync,
|
||||
application.sessions,
|
||||
application.preferences,
|
||||
application.componentManager,
|
||||
application.alerts,
|
||||
application.isNativeMobileWebUseCase,
|
||||
)
|
||||
await controller.initialize()
|
||||
|
||||
expect(application.items.createTemplateItem).toHaveBeenCalledWith(
|
||||
@@ -86,7 +107,18 @@ describe('note view controller', () => {
|
||||
application.items.findItem = jest.fn().mockReturnValue(tag)
|
||||
application.mutator.addTagToNote = jest.fn()
|
||||
|
||||
const controller = new NoteViewController(application, undefined, { tag: tag.uuid })
|
||||
const controller = new NoteViewController(
|
||||
undefined,
|
||||
application.items,
|
||||
application.mutator,
|
||||
application.sync,
|
||||
application.sessions,
|
||||
application.preferences,
|
||||
application.componentManager,
|
||||
application.alerts,
|
||||
application.isNativeMobileWebUseCase,
|
||||
{ tag: tag.uuid },
|
||||
)
|
||||
await controller.initialize()
|
||||
|
||||
expect(controller['defaultTag']).toEqual(tag)
|
||||
@@ -100,7 +132,17 @@ describe('note view controller', () => {
|
||||
|
||||
application.items.findItem = jest.fn().mockReturnValue(note)
|
||||
|
||||
const controller = new NoteViewController(application, note)
|
||||
const controller = new NoteViewController(
|
||||
note,
|
||||
application.items,
|
||||
application.mutator,
|
||||
application.sync,
|
||||
application.sessions,
|
||||
application.preferences,
|
||||
application.componentManager,
|
||||
application.alerts,
|
||||
application.isNativeMobileWebUseCase,
|
||||
)
|
||||
await controller.initialize()
|
||||
|
||||
const changePromise = Deferred()
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { WebApplication } from '@/Application/WebApplication'
|
||||
import { noteTypeForEditorIdentifier } from '@standardnotes/features'
|
||||
import {
|
||||
SNNote,
|
||||
@@ -9,13 +8,23 @@ import {
|
||||
PrefKey,
|
||||
PayloadVaultOverrides,
|
||||
} from '@standardnotes/models'
|
||||
import { UuidString } from '@standardnotes/snjs'
|
||||
import {
|
||||
AlertService,
|
||||
ComponentManagerInterface,
|
||||
ItemManagerInterface,
|
||||
MutatorClientInterface,
|
||||
PreferenceServiceInterface,
|
||||
SessionsClientInterface,
|
||||
SyncServiceInterface,
|
||||
UuidString,
|
||||
} from '@standardnotes/snjs'
|
||||
import { removeFromArray } from '@standardnotes/utils'
|
||||
import { ContentType } from '@standardnotes/domain-core'
|
||||
import { ItemViewControllerInterface } from './ItemViewControllerInterface'
|
||||
import { TemplateNoteViewControllerOptions } from './TemplateNoteViewControllerOptions'
|
||||
import { log, LoggingDomain } from '@/Logging'
|
||||
import { NoteSaveFunctionParams, NoteSyncController } from '../../../Controllers/NoteSyncController'
|
||||
import { IsNativeMobileWeb } from '@standardnotes/ui-services'
|
||||
|
||||
export type EditorValues = {
|
||||
title: string
|
||||
@@ -37,8 +46,15 @@ export class NoteViewController implements ItemViewControllerInterface {
|
||||
private syncController!: NoteSyncController
|
||||
|
||||
constructor(
|
||||
private application: WebApplication,
|
||||
item?: SNNote,
|
||||
item: SNNote | undefined,
|
||||
private items: ItemManagerInterface,
|
||||
private mutator: MutatorClientInterface,
|
||||
private sync: SyncServiceInterface,
|
||||
private sessions: SessionsClientInterface,
|
||||
private preferences: PreferenceServiceInterface,
|
||||
private components: ComponentManagerInterface,
|
||||
private alerts: AlertService,
|
||||
private _isNativeMobileWeb: IsNativeMobileWeb,
|
||||
public templateNoteOptions?: TemplateNoteViewControllerOptions,
|
||||
) {
|
||||
if (item) {
|
||||
@@ -50,10 +66,18 @@ export class NoteViewController implements ItemViewControllerInterface {
|
||||
}
|
||||
|
||||
if (this.defaultTagUuid) {
|
||||
this.defaultTag = this.application.items.findItem(this.defaultTagUuid) as SNTag
|
||||
this.defaultTag = this.items.findItem(this.defaultTagUuid) as SNTag
|
||||
}
|
||||
|
||||
this.syncController = new NoteSyncController(this.application, this.item)
|
||||
this.syncController = new NoteSyncController(
|
||||
this.item,
|
||||
this.items,
|
||||
this.mutator,
|
||||
this.sessions,
|
||||
this.sync,
|
||||
this.alerts,
|
||||
this._isNativeMobileWeb,
|
||||
)
|
||||
}
|
||||
|
||||
deinit(): void {
|
||||
@@ -74,9 +98,6 @@ export class NoteViewController implements ItemViewControllerInterface {
|
||||
disposer()
|
||||
}
|
||||
this.disposers.length = 0
|
||||
;(this.application as unknown) = undefined
|
||||
;(this.item as unknown) = undefined
|
||||
|
||||
this.innerValueChangeObservers.length = 0
|
||||
}
|
||||
|
||||
@@ -89,16 +110,16 @@ export class NoteViewController implements ItemViewControllerInterface {
|
||||
|
||||
this.needsInit = false
|
||||
|
||||
const addTagHierarchy = this.application.getPreference(PrefKey.NoteAddToParentFolders, true)
|
||||
const addTagHierarchy = this.preferences.getValue(PrefKey.NoteAddToParentFolders, true)
|
||||
|
||||
if (!this.item) {
|
||||
log(LoggingDomain.NoteView, 'Initializing as template note')
|
||||
|
||||
const editorIdentifier = this.application.componentManager.getDefaultEditorIdentifier(this.defaultTag)
|
||||
const editorIdentifier = this.components.getDefaultEditorIdentifier(this.defaultTag)
|
||||
|
||||
const noteType = noteTypeForEditorIdentifier(editorIdentifier)
|
||||
|
||||
const note = this.application.items.createTemplateItem<NoteContent, SNNote>(
|
||||
const note = this.items.createTemplateItem<NoteContent, SNNote>(
|
||||
ContentType.TYPES.Note,
|
||||
{
|
||||
text: '',
|
||||
@@ -118,8 +139,8 @@ export class NoteViewController implements ItemViewControllerInterface {
|
||||
this.syncController.setItem(this.item)
|
||||
|
||||
if (this.defaultTagUuid) {
|
||||
const tag = this.application.items.findItem(this.defaultTagUuid) as SNTag
|
||||
await this.application.mutator.addTagToNote(note, tag, addTagHierarchy)
|
||||
const tag = this.items.findItem(this.defaultTagUuid) as SNTag
|
||||
await this.mutator.addTagToNote(note, tag, addTagHierarchy)
|
||||
}
|
||||
|
||||
this.notifyObservers(this.item, PayloadEmitSource.InitialObserverRegistrationPush)
|
||||
@@ -140,7 +161,7 @@ export class NoteViewController implements ItemViewControllerInterface {
|
||||
}
|
||||
|
||||
this.disposers.push(
|
||||
this.application.streamItems<SNNote>(ContentType.TYPES.Note, ({ changed, inserted, source }) => {
|
||||
this.items.streamItems<SNNote>(ContentType.TYPES.Note, ({ changed, inserted, source }) => {
|
||||
if (this.dealloced) {
|
||||
return
|
||||
}
|
||||
@@ -163,7 +184,7 @@ export class NoteViewController implements ItemViewControllerInterface {
|
||||
public insertTemplatedNote(): Promise<DecryptedItemInterface> {
|
||||
log(LoggingDomain.NoteView, 'Inserting template note')
|
||||
this.isTemplateNote = false
|
||||
return this.application.mutator.insertItem(this.item)
|
||||
return this.mutator.insertItem(this.item)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -105,7 +105,7 @@ const NoteConflictResolutionModal = ({
|
||||
mutator.conflictOf = undefined
|
||||
})
|
||||
setIsPerformingAction(false)
|
||||
void application.controllers.selectionController.selectItem(selectedNotes[0].uuid, true)
|
||||
void application.itemListController.selectItem(selectedNotes[0].uuid, true)
|
||||
void application.sync.sync()
|
||||
close()
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
*/
|
||||
|
||||
import { WebApplication } from '@/Application/WebApplication'
|
||||
import { ViewControllerManager } from '@/Controllers/ViewControllerManager'
|
||||
import { NotesController } from '@/Controllers/NotesController/NotesController'
|
||||
import {
|
||||
ApplicationEvent,
|
||||
@@ -18,7 +17,7 @@ import { NoteViewController } from './Controller/NoteViewController'
|
||||
describe('NoteView', () => {
|
||||
let noteViewController: NoteViewController
|
||||
let application: WebApplication
|
||||
let viewControllerManager: ViewControllerManager
|
||||
|
||||
let notesController: NotesController
|
||||
|
||||
const createNoteView = () =>
|
||||
@@ -37,13 +36,11 @@ describe('NoteView', () => {
|
||||
notesController.getSpellcheckStateForNote = jest.fn()
|
||||
notesController.getEditorWidthForNote = jest.fn()
|
||||
|
||||
viewControllerManager = {
|
||||
notesController: notesController,
|
||||
} as jest.Mocked<ViewControllerManager>
|
||||
|
||||
application = {
|
||||
controllers: viewControllerManager,
|
||||
} as jest.Mocked<WebApplication>
|
||||
notesController,
|
||||
noteViewController,
|
||||
} as unknown as jest.Mocked<WebApplication>
|
||||
|
||||
application.hasProtectionSources = jest.fn().mockReturnValue(true)
|
||||
application.authorizeNoteAccess = jest.fn()
|
||||
application.addWebEventObserver = jest.fn()
|
||||
|
||||
@@ -111,7 +111,7 @@ class NoteView extends AbstractComponent<NoteViewProps, State> {
|
||||
if (!this.controller || this.controller.dealloced) {
|
||||
return
|
||||
}
|
||||
this.application.getDesktopService()?.redoSearch()
|
||||
this.application.desktopManager?.redoSearch()
|
||||
}
|
||||
|
||||
this.debounceReloadEditorComponent = debounce(this.debounceReloadEditorComponent.bind(this), 25)
|
||||
@@ -215,7 +215,7 @@ class NoteView extends AbstractComponent<NoteViewProps, State> {
|
||||
|
||||
this.autorun(() => {
|
||||
this.setState({
|
||||
showProtectedWarning: this.viewControllerManager.notesController.showProtectedWarning,
|
||||
showProtectedWarning: this.application.notesController.showProtectedWarning,
|
||||
})
|
||||
})
|
||||
|
||||
@@ -224,7 +224,7 @@ class NoteView extends AbstractComponent<NoteViewProps, State> {
|
||||
|
||||
const showProtectedWarning =
|
||||
this.note.protected &&
|
||||
(!this.application.hasProtectionSources() || !this.application.hasUnprotectedAccessSession())
|
||||
(!this.application.hasProtectionSources() || !this.application.protections.hasUnprotectedAccessSession())
|
||||
this.setShowProtectedOverlay(showProtectedWarning)
|
||||
|
||||
this.reloadPreferences().catch(console.error)
|
||||
@@ -429,7 +429,7 @@ class NoteView extends AbstractComponent<NoteViewProps, State> {
|
||||
}
|
||||
|
||||
streamItems() {
|
||||
this.removeNoteStreamObserver = this.application.streamItems<SNNote>(ContentType.TYPES.Note, async () => {
|
||||
this.removeNoteStreamObserver = this.application.items.streamItems<SNNote>(ContentType.TYPES.Note, async () => {
|
||||
if (!this.note) {
|
||||
return
|
||||
}
|
||||
@@ -541,7 +541,7 @@ class NoteView extends AbstractComponent<NoteViewProps, State> {
|
||||
})
|
||||
this.setStatus({
|
||||
type: 'saved',
|
||||
message: 'All changes saved' + (this.application.noAccount() ? ' offline' : ''),
|
||||
message: 'All changes saved' + (this.application.sessions.isSignedOut() ? ' offline' : ''),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -615,7 +615,7 @@ class NoteView extends AbstractComponent<NoteViewProps, State> {
|
||||
}
|
||||
|
||||
setShowProtectedOverlay(show: boolean) {
|
||||
this.viewControllerManager.notesController.setShowProtectedWarning(show)
|
||||
this.application.notesController.setShowProtectedWarning(show)
|
||||
}
|
||||
|
||||
async deleteNote(permanently: boolean) {
|
||||
@@ -677,7 +677,7 @@ class NoteView extends AbstractComponent<NoteViewProps, State> {
|
||||
}
|
||||
|
||||
async reloadSpellcheck() {
|
||||
const spellcheck = this.viewControllerManager.notesController.getSpellcheckStateForNote(this.note)
|
||||
const spellcheck = this.application.notesController.getSpellcheckStateForNote(this.note)
|
||||
if (spellcheck !== this.state.spellcheck) {
|
||||
reloadFont(this.state.monospaceFont)
|
||||
this.setState({ spellcheck })
|
||||
@@ -685,7 +685,7 @@ class NoteView extends AbstractComponent<NoteViewProps, State> {
|
||||
}
|
||||
|
||||
reloadLineWidth() {
|
||||
const editorLineWidth = this.viewControllerManager.notesController.getEditorWidthForNote(this.note)
|
||||
const editorLineWidth = this.application.notesController.getEditorWidthForNote(this.note)
|
||||
|
||||
this.setState({
|
||||
editorLineWidth,
|
||||
@@ -849,15 +849,15 @@ class NoteView extends AbstractComponent<NoteViewProps, State> {
|
||||
{this.note && (
|
||||
<NoteViewFileDropTarget
|
||||
note={this.note}
|
||||
linkingController={this.viewControllerManager.linkingController}
|
||||
filesController={this.viewControllerManager.filesController}
|
||||
linkingController={this.application.linkingController}
|
||||
filesController={this.application.filesController}
|
||||
noteViewElement={this.noteViewElementRef.current}
|
||||
/>
|
||||
)}
|
||||
|
||||
{this.state.noteLocked && (
|
||||
<EditingDisabledBanner
|
||||
onClick={() => this.viewControllerManager.notesController.setLockSelectedNotes(!this.state.noteLocked)}
|
||||
onClick={() => this.application.notesController.setLockSelectedNotes(!this.state.noteLocked)}
|
||||
noteLocked={this.state.noteLocked}
|
||||
/>
|
||||
)}
|
||||
@@ -913,36 +913,26 @@ class NoteView extends AbstractComponent<NoteViewProps, State> {
|
||||
<div className="note-view-options-buttons flex items-center gap-3">
|
||||
<CollaborationInfoHUD item={this.note} />
|
||||
<LinkedItemsButton
|
||||
filesController={this.viewControllerManager.filesController}
|
||||
linkingController={this.viewControllerManager.linkingController}
|
||||
linkingController={this.application.linkingController}
|
||||
onClickPreprocessing={this.ensureNoteIsInsertedBeforeUIAction}
|
||||
featuresController={this.viewControllerManager.featuresController}
|
||||
/>
|
||||
<ChangeEditorButton
|
||||
viewControllerManager={this.viewControllerManager}
|
||||
noteViewController={this.controller}
|
||||
onClickPreprocessing={this.ensureNoteIsInsertedBeforeUIAction}
|
||||
/>
|
||||
<PinNoteButton
|
||||
notesController={this.viewControllerManager.notesController}
|
||||
notesController={this.application.notesController}
|
||||
onClickPreprocessing={this.ensureNoteIsInsertedBeforeUIAction}
|
||||
/>
|
||||
<NotesOptionsPanel
|
||||
navigationController={this.viewControllerManager.navigationController}
|
||||
notesController={this.viewControllerManager.notesController}
|
||||
linkingController={this.viewControllerManager.linkingController}
|
||||
historyModalController={this.viewControllerManager.historyModalController}
|
||||
selectionController={this.viewControllerManager.selectionController}
|
||||
notesController={this.application.notesController}
|
||||
onClickPreprocessing={this.ensureNoteIsInsertedBeforeUIAction}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="hidden md:block">
|
||||
<LinkedItemBubblesContainer
|
||||
item={this.note}
|
||||
linkingController={this.viewControllerManager.linkingController}
|
||||
/>
|
||||
<LinkedItemBubblesContainer item={this.note} linkingController={this.application.linkingController} />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
@@ -990,8 +980,8 @@ class NoteView extends AbstractComponent<NoteViewProps, State> {
|
||||
<SuperEditor
|
||||
key={this.note.uuid}
|
||||
application={this.application}
|
||||
linkingController={this.viewControllerManager.linkingController}
|
||||
filesController={this.viewControllerManager.filesController}
|
||||
linkingController={this.application.linkingController}
|
||||
filesController={this.application.filesController}
|
||||
spellcheck={this.state.spellcheck}
|
||||
controller={this.controller}
|
||||
/>
|
||||
|
||||
@@ -24,7 +24,7 @@ const NoteViewFileDropTarget = ({ note, linkingController, noteViewElement, file
|
||||
tooltipText: 'Drop your files to upload and link them to the current note',
|
||||
callback: async (uploadedFile) => {
|
||||
await linkingController.linkItems(note, uploadedFile)
|
||||
void application.changeAndSaveItem(uploadedFile, (mutator) => {
|
||||
void application.changeAndSaveItem.execute(uploadedFile, (mutator) => {
|
||||
mutator.protected = note.protected
|
||||
})
|
||||
filesController.notifyObserversOfUploadedFileLinkingToCurrentNote(uploadedFile.uuid)
|
||||
|
||||
@@ -46,6 +46,7 @@ export const PlainEditor = forwardRef<PlainEditorInterface, Props>(
|
||||
const note = useRef(controller.item)
|
||||
|
||||
const tabObserverDisposer = useRef<Disposer>()
|
||||
const mutationObserver = useRef<MutationObserver | null>(null)
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
focus() {
|
||||
@@ -53,6 +54,15 @@ export const PlainEditor = forwardRef<PlainEditorInterface, Props>(
|
||||
},
|
||||
}))
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
mutationObserver.current?.disconnect()
|
||||
tabObserverDisposer.current?.()
|
||||
tabObserverDisposer.current = undefined
|
||||
mutationObserver.current = null
|
||||
}
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
const disposer = controller.addNoteInnerValueChangeObserver((updatedNote, source) => {
|
||||
if (updatedNote.uuid !== note.current.uuid) {
|
||||
@@ -219,15 +229,18 @@ export const PlainEditor = forwardRef<PlainEditorInterface, Props>(
|
||||
const observer = new MutationObserver((records) => {
|
||||
for (const record of records) {
|
||||
record.removedNodes.forEach((node) => {
|
||||
if (node === editor) {
|
||||
if (node.isEqualNode(editor)) {
|
||||
tabObserverDisposer.current?.()
|
||||
tabObserverDisposer.current = undefined
|
||||
observer.disconnect()
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
observer.observe(editor.parentElement as HTMLElement, { childList: true })
|
||||
|
||||
mutationObserver.current = observer
|
||||
}
|
||||
|
||||
if (textareaUnloading) {
|
||||
|
||||
Reference in New Issue
Block a user