feat: Editing large notes (greater than 1.5MB) will result in more optimized syncing, in which changes are saved locally immediately, but sync with the server less frequently (roughly every 30 seconds rather than after every change). (#2768)
This commit is contained in:
@@ -54,8 +54,6 @@ import ModalOverlay from '../Modal/ModalOverlay'
|
||||
import NoteConflictResolutionModal from './NoteConflictResolutionModal/NoteConflictResolutionModal'
|
||||
import Icon from '../Icon/Icon'
|
||||
|
||||
const MinimumStatusDuration = 400
|
||||
|
||||
function sortAlphabetically(array: ComponentInterface[]): ComponentInterface[] {
|
||||
return array.sort((a, b) => (a.name.toLowerCase() < b.name.toLowerCase() ? -1 : 1))
|
||||
}
|
||||
@@ -83,6 +81,7 @@ type State = {
|
||||
updateSavingIndicator?: boolean
|
||||
editorFeatureIdentifier?: string
|
||||
noteType?: NoteType
|
||||
focusModeEnabled?: boolean
|
||||
|
||||
conflictedNotes: SNNote[]
|
||||
showConflictResolutionModal: boolean
|
||||
@@ -91,7 +90,6 @@ type State = {
|
||||
class NoteView extends AbstractComponent<NoteViewProps, State> {
|
||||
readonly controller!: NoteViewController
|
||||
|
||||
private statusTimeout?: NodeJS.Timeout
|
||||
onEditorComponentLoad?: () => void
|
||||
|
||||
private removeTrashKeyObserver?: () => void
|
||||
@@ -167,8 +165,6 @@ class NoteView extends AbstractComponent<NoteViewProps, State> {
|
||||
;(this.ensureNoteIsInsertedBeforeUIAction as unknown) = undefined
|
||||
|
||||
this.onEditorComponentLoad = undefined
|
||||
|
||||
this.statusTimeout = undefined
|
||||
;(this.onPanelResizeFinish as unknown) = undefined
|
||||
;(this.authorizeAndDismissProtectedWarning as unknown) = undefined
|
||||
;(this.editorComponentViewerRequestsReload as unknown) = undefined
|
||||
@@ -235,9 +231,22 @@ class NoteView extends AbstractComponent<NoteViewProps, State> {
|
||||
})
|
||||
|
||||
this.autorun(() => {
|
||||
const syncStatus = this.controller.syncStatus
|
||||
|
||||
const isFocusModeEnabled = this.application.paneController.focusModeEnabled
|
||||
const didFocusModeChange = this.state.focusModeEnabled !== isFocusModeEnabled
|
||||
|
||||
this.setState({
|
||||
showProtectedWarning: this.application.notesController.showProtectedWarning,
|
||||
noteStatus: syncStatus,
|
||||
saveError: syncStatus?.type === 'error',
|
||||
syncTakingTooLong: false,
|
||||
focusModeEnabled: isFocusModeEnabled,
|
||||
})
|
||||
|
||||
if (!isFocusModeEnabled && didFocusModeChange) {
|
||||
this.controller.syncOnlyIfLargeNote()
|
||||
}
|
||||
})
|
||||
|
||||
this.reloadEditorComponent().catch(console.error)
|
||||
@@ -327,16 +336,18 @@ class NoteView extends AbstractComponent<NoteViewProps, State> {
|
||||
}
|
||||
|
||||
if (note.lastSyncBegan || note.dirty) {
|
||||
const currentStatus = this.controller.syncStatus
|
||||
const isWaitingToSyncLargeNote = currentStatus?.type === 'waiting'
|
||||
if (note.lastSyncEnd) {
|
||||
const shouldShowSavingStatus = note.lastSyncBegan && note.lastSyncBegan.getTime() > note.lastSyncEnd.getTime()
|
||||
const hasStartedNewSync = note.lastSyncBegan && note.lastSyncBegan.getTime() > note.lastSyncEnd.getTime()
|
||||
const shouldShowSavedStatus = note.lastSyncBegan && note.lastSyncEnd.getTime() > note.lastSyncBegan.getTime()
|
||||
if (note.dirty || shouldShowSavingStatus) {
|
||||
this.showSavingStatus()
|
||||
} else if (this.state.noteStatus && shouldShowSavedStatus) {
|
||||
this.showAllChangesSavedStatus()
|
||||
if (hasStartedNewSync) {
|
||||
this.controller.showSavingStatus()
|
||||
} else if (this.state.noteStatus && shouldShowSavedStatus && !isWaitingToSyncLargeNote) {
|
||||
this.controller.showAllChangesSavedStatus()
|
||||
}
|
||||
} else {
|
||||
this.showSavingStatus()
|
||||
} else if (note.lastSyncBegan) {
|
||||
this.controller.showSavingStatus()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -372,7 +383,7 @@ class NoteView extends AbstractComponent<NoteViewProps, State> {
|
||||
const isInErrorState = this.state.saveError
|
||||
/** if we're still dirty, don't change status, a sync is likely upcoming. */
|
||||
if (!this.note.dirty && isInErrorState) {
|
||||
this.showAllChangesSavedStatus()
|
||||
this.controller.showAllChangesSavedStatus()
|
||||
}
|
||||
break
|
||||
}
|
||||
@@ -383,14 +394,14 @@ class NoteView extends AbstractComponent<NoteViewProps, State> {
|
||||
* and we don't want to display an error here.
|
||||
*/
|
||||
if (this.note.dirty) {
|
||||
this.showErrorStatus()
|
||||
this.controller.showErrorSyncStatus()
|
||||
}
|
||||
break
|
||||
case ApplicationEvent.LocalDatabaseWriteError:
|
||||
this.showErrorStatus({
|
||||
this.controller.showErrorSyncStatus({
|
||||
type: 'error',
|
||||
message: 'Offline Saving Issue',
|
||||
desc: 'Changes not saved',
|
||||
description: 'Changes not saved',
|
||||
})
|
||||
break
|
||||
case ApplicationEvent.UnprotectedSessionBegan: {
|
||||
@@ -551,59 +562,6 @@ class NoteView extends AbstractComponent<NoteViewProps, State> {
|
||||
return this.application.actions.extensionsInContextOfItem(this.note).length > 0
|
||||
}
|
||||
|
||||
showSavingStatus() {
|
||||
this.setStatus({ type: 'saving', message: 'Saving…' }, false)
|
||||
}
|
||||
|
||||
showAllChangesSavedStatus() {
|
||||
this.setState({
|
||||
saveError: false,
|
||||
syncTakingTooLong: false,
|
||||
})
|
||||
this.setStatus({
|
||||
type: 'saved',
|
||||
message: 'All changes saved' + (this.application.sessions.isSignedOut() ? ' offline' : ''),
|
||||
})
|
||||
}
|
||||
|
||||
showErrorStatus(error?: NoteStatus) {
|
||||
if (!error) {
|
||||
error = {
|
||||
type: 'error',
|
||||
message: 'Sync Unreachable',
|
||||
desc: 'Changes saved offline',
|
||||
}
|
||||
}
|
||||
this.setState({
|
||||
saveError: true,
|
||||
syncTakingTooLong: false,
|
||||
})
|
||||
this.setStatus(error)
|
||||
}
|
||||
|
||||
setStatus(status: NoteStatus, wait = true) {
|
||||
if (this.statusTimeout) {
|
||||
clearTimeout(this.statusTimeout)
|
||||
}
|
||||
if (wait) {
|
||||
this.statusTimeout = setTimeout(() => {
|
||||
this.setState({
|
||||
noteStatus: status,
|
||||
})
|
||||
}, MinimumStatusDuration)
|
||||
} else {
|
||||
this.setState({
|
||||
noteStatus: status,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
cancelPendingSetStatus() {
|
||||
if (this.statusTimeout) {
|
||||
clearTimeout(this.statusTimeout)
|
||||
}
|
||||
}
|
||||
|
||||
onTitleEnter: KeyboardEventHandler<HTMLInputElement> = ({ key, currentTarget }) => {
|
||||
if (key !== KeyboardKey.Enter) {
|
||||
return
|
||||
@@ -839,6 +797,10 @@ class NoteView extends AbstractComponent<NoteViewProps, State> {
|
||||
}))
|
||||
}
|
||||
|
||||
triggerSyncOnAction = () => {
|
||||
this.controller.syncNow()
|
||||
}
|
||||
|
||||
override render() {
|
||||
if (this.controller.dealloced) {
|
||||
return null
|
||||
@@ -923,6 +885,7 @@ class NoteView extends AbstractComponent<NoteViewProps, State> {
|
||||
/>
|
||||
</div>
|
||||
<NoteStatusIndicator
|
||||
note={this.note}
|
||||
status={this.state.noteStatus}
|
||||
syncTakingTooLong={this.state.syncTakingTooLong}
|
||||
updateSavingIndicator={this.state.updateSavingIndicator}
|
||||
@@ -930,6 +893,7 @@ class NoteView extends AbstractComponent<NoteViewProps, State> {
|
||||
</div>
|
||||
{shouldShowConflictsButton && (
|
||||
<Button
|
||||
id={ElementIds.ConflictResolutionButton}
|
||||
className="flex items-center"
|
||||
primary
|
||||
colorStyle="warning"
|
||||
@@ -946,10 +910,12 @@ class NoteView extends AbstractComponent<NoteViewProps, State> {
|
||||
<>
|
||||
<LinkedItemsButton
|
||||
linkingController={this.application.linkingController}
|
||||
onClick={this.triggerSyncOnAction}
|
||||
onClickPreprocessing={this.ensureNoteIsInsertedBeforeUIAction}
|
||||
/>
|
||||
<ChangeEditorButton
|
||||
noteViewController={this.controller}
|
||||
onClick={this.triggerSyncOnAction}
|
||||
onClickPreprocessing={this.ensureNoteIsInsertedBeforeUIAction}
|
||||
/>
|
||||
<PinNoteButton
|
||||
@@ -960,6 +926,7 @@ class NoteView extends AbstractComponent<NoteViewProps, State> {
|
||||
)}
|
||||
<NotesOptionsPanel
|
||||
notesController={this.application.notesController}
|
||||
onClick={this.triggerSyncOnAction}
|
||||
onClickPreprocessing={this.ensureNoteIsInsertedBeforeUIAction}
|
||||
onButtonBlur={() => {
|
||||
this.setState({
|
||||
|
||||
Reference in New Issue
Block a user