refactor: avoid deallocing note controller until local propagation complete

This commit is contained in:
Mo
2022-12-05 07:53:21 -06:00
parent 46b19d8caf
commit bd5ccbfab6
2 changed files with 59 additions and 8 deletions

View File

@@ -7,6 +7,8 @@ import {
SNTag,
ItemsClientInterface,
SNNote,
Deferred,
SyncServiceInterface,
} from '@standardnotes/snjs'
import { FeatureIdentifier, NoteType } from '@standardnotes/features'
import { NoteViewController } from './NoteViewController'
@@ -17,10 +19,16 @@ describe('note view controller', () => {
beforeEach(() => {
application = {} as jest.Mocked<WebApplication>
application.streamItems = jest.fn()
application.streamItems = jest.fn().mockReturnValue(() => {})
application.getPreference = jest.fn().mockReturnValue(true)
application.noAccount = jest.fn().mockReturnValue(false)
application.isNativeMobileWeb = jest.fn().mockReturnValue(false)
Object.defineProperty(application, 'items', { value: {} as jest.Mocked<ItemsClientInterface> })
Object.defineProperty(application, 'sync', { value: {} as jest.Mocked<SyncServiceInterface> })
application.sync.sync = jest.fn().mockReturnValue(Promise.resolve())
componentManager = {} as jest.Mocked<SNComponentManager>
componentManager.legacyGetDefaultEditor = jest.fn()
Object.defineProperty(application, 'componentManager', { value: componentManager })
@@ -80,4 +88,30 @@ describe('note view controller', () => {
expect(controller['defaultTag']).toEqual(tag)
expect(application.items.addTagToNote).toHaveBeenCalledWith(expect.anything(), tag, expect.anything())
})
it('should wait until item finishes saving locally before deiniting', async () => {
const note = {
uuid: 'note-uuid',
} as jest.Mocked<SNNote>
application.items.findItem = jest.fn().mockReturnValue(note)
const controller = new NoteViewController(application, note)
await controller.initialize()
const changePromise = Deferred()
application.mutator.changeItem = jest.fn().mockReturnValue(changePromise.promise)
const savePromise = controller.saveAndAwaitLocalPropagation({ isUserModified: true, bypassDebouncer: true })
controller.deinit()
expect(controller.dealloced).toEqual(false)
changePromise.resolve(true)
await changePromise.promise
await savePromise
expect(controller.dealloced).toEqual(true)
})
})

View File

@@ -11,7 +11,7 @@ import {
PrefKey,
} from '@standardnotes/models'
import { UuidString } from '@standardnotes/snjs'
import { removeFromArray } from '@standardnotes/utils'
import { removeFromArray, Deferred } from '@standardnotes/utils'
import { ContentType } from '@standardnotes/common'
import { ItemViewControllerInterface } from './ItemViewControllerInterface'
import { TemplateNoteViewControllerOptions } from './TemplateNoteViewControllerOptions'
@@ -29,14 +29,16 @@ const NotePreviewCharLimit = 160
export class NoteViewController implements ItemViewControllerInterface {
public item!: SNNote
public dealloced = false
public isTemplateNote = false
public runtimeId = `${Math.random()}`
public needsInit = true
private innerValueChangeObservers: ((note: SNNote, source: PayloadEmitSource) => void)[] = []
private disposers: (() => void)[] = []
public isTemplateNote = false
private saveTimeout?: ReturnType<typeof setTimeout>
private defaultTagUuid: UuidString | undefined
private defaultTag?: SNTag
public runtimeId = `${Math.random()}`
public needsInit = true
private savingLocallyPromise = Deferred<void>()
constructor(
private application: WebApplication,
@@ -57,7 +59,14 @@ export class NoteViewController implements ItemViewControllerInterface {
}
deinit(): void {
void this.savingLocallyPromise.promise.then(() => {
this.performDeinitSafely()
})
}
private performDeinitSafely(): void {
this.dealloced = true
for (const disposer of this.disposers) {
disposer()
}
@@ -184,6 +193,8 @@ export class NoteViewController implements ItemViewControllerInterface {
throw Error('NoteViewController not initialized')
}
this.savingLocallyPromise = Deferred<void>()
if (this.saveTimeout) {
clearTimeout(this.saveTimeout)
}
@@ -198,7 +209,13 @@ export class NoteViewController implements ItemViewControllerInterface {
return new Promise((resolve) => {
this.saveTimeout = setTimeout(() => {
void this.undebouncedSave({ ...params, onLocalPropagationComplete: resolve })
void this.undebouncedSave({
...params,
onLocalPropagationComplete: () => {
this.savingLocallyPromise.resolve()
resolve()
},
})
}, syncDebouceMs)
})
}
@@ -262,10 +279,10 @@ export class NoteViewController implements ItemViewControllerInterface {
params.isUserModified,
)
params.onLocalPropagationComplete?.()
void this.application.sync.sync().then(() => {
params.onRemoteSyncComplete?.()
})
params.onLocalPropagationComplete?.()
}
}