133 lines
3.9 KiB
TypeScript
133 lines
3.9 KiB
TypeScript
import { MutationType, NoteMutator, SNNote } from '@standardnotes/models'
|
|
import {
|
|
AlertService,
|
|
InfoStrings,
|
|
ItemManagerInterface,
|
|
MutatorClientInterface,
|
|
SessionsClientInterface,
|
|
SyncServiceInterface,
|
|
} from '@standardnotes/snjs'
|
|
import { Deferred } from '@standardnotes/utils'
|
|
import { EditorSaveTimeoutDebounce } from '../Components/NoteView/Controller/EditorSaveTimeoutDebounce'
|
|
import { IsNativeMobileWeb } from '@standardnotes/ui-services'
|
|
|
|
const NotePreviewCharLimit = 160
|
|
|
|
export type NoteSaveFunctionParams = {
|
|
title?: string
|
|
text?: string
|
|
bypassDebouncer?: boolean
|
|
isUserModified?: boolean
|
|
dontGeneratePreviews?: boolean
|
|
previews?: { previewPlain: string; previewHtml?: string }
|
|
customMutate?: (mutator: NoteMutator) => void
|
|
onLocalPropagationComplete?: () => void
|
|
onRemoteSyncComplete?: () => void
|
|
}
|
|
|
|
export class NoteSyncController {
|
|
savingLocallyPromise: ReturnType<typeof Deferred<void>> | null = null
|
|
|
|
private saveTimeout?: ReturnType<typeof setTimeout>
|
|
|
|
constructor(
|
|
private item: SNNote,
|
|
private items: ItemManagerInterface,
|
|
private mutator: MutatorClientInterface,
|
|
private sessions: SessionsClientInterface,
|
|
private sync: SyncServiceInterface,
|
|
private alerts: AlertService,
|
|
private _isNativeMobileWeb: IsNativeMobileWeb,
|
|
) {}
|
|
|
|
setItem(item: SNNote) {
|
|
this.item = item
|
|
}
|
|
|
|
deinit() {
|
|
if (this.saveTimeout) {
|
|
clearTimeout(this.saveTimeout)
|
|
}
|
|
if (this.savingLocallyPromise) {
|
|
this.savingLocallyPromise.reject()
|
|
}
|
|
this.savingLocallyPromise = null
|
|
this.saveTimeout = undefined
|
|
;(this.item as unknown) = undefined
|
|
}
|
|
|
|
public async saveAndAwaitLocalPropagation(params: NoteSaveFunctionParams): Promise<void> {
|
|
this.savingLocallyPromise = Deferred<void>()
|
|
|
|
if (this.saveTimeout) {
|
|
clearTimeout(this.saveTimeout)
|
|
}
|
|
|
|
const noDebounce = params.bypassDebouncer || this.sessions.isSignedOut()
|
|
|
|
const syncDebouceMs = noDebounce
|
|
? EditorSaveTimeoutDebounce.ImmediateChange
|
|
: this._isNativeMobileWeb.execute().getValue()
|
|
? EditorSaveTimeoutDebounce.NativeMobileWeb
|
|
: EditorSaveTimeoutDebounce.Desktop
|
|
|
|
return new Promise((resolve) => {
|
|
this.saveTimeout = setTimeout(() => {
|
|
void this.undebouncedSave({
|
|
...params,
|
|
onLocalPropagationComplete: () => {
|
|
if (this.savingLocallyPromise) {
|
|
this.savingLocallyPromise.resolve()
|
|
}
|
|
resolve()
|
|
},
|
|
})
|
|
}, syncDebouceMs)
|
|
})
|
|
}
|
|
|
|
private async undebouncedSave(params: NoteSaveFunctionParams): Promise<void> {
|
|
if (!this.items.findItem(this.item.uuid)) {
|
|
void this.alerts.alert(InfoStrings.InvalidNote)
|
|
return
|
|
}
|
|
|
|
await this.mutator.changeItem(
|
|
this.item,
|
|
(mutator) => {
|
|
const noteMutator = mutator as NoteMutator
|
|
if (params.customMutate) {
|
|
params.customMutate(noteMutator)
|
|
}
|
|
|
|
if (params.title != undefined) {
|
|
noteMutator.title = params.title
|
|
}
|
|
|
|
if (params.text != undefined) {
|
|
noteMutator.text = params.text
|
|
}
|
|
|
|
if (params.previews) {
|
|
noteMutator.preview_plain = params.previews.previewPlain
|
|
noteMutator.preview_html = params.previews.previewHtml
|
|
} else if (!params.dontGeneratePreviews && params.text != undefined) {
|
|
const noteText = params.text || ''
|
|
const truncate = noteText.length > NotePreviewCharLimit
|
|
const substring = noteText.substring(0, NotePreviewCharLimit)
|
|
const previewPlain = substring + (truncate ? '...' : '')
|
|
noteMutator.preview_plain = previewPlain
|
|
noteMutator.preview_html = undefined
|
|
}
|
|
},
|
|
params.isUserModified ? MutationType.UpdateUserTimestamps : MutationType.NoUpdateUserTimestamps,
|
|
)
|
|
|
|
void this.sync.sync().then(() => {
|
|
params.onRemoteSyncComplete?.()
|
|
})
|
|
|
|
params.onLocalPropagationComplete?.()
|
|
}
|
|
}
|