From f95ee689fdda2e30c32a99df5b9692c38b620c8f Mon Sep 17 00:00:00 2001 From: Laurent Cozic Date: Wed, 3 Jan 2024 18:02:05 +0000 Subject: [PATCH] Mobile: Improve sync by reducing how often note list is sorted --- packages/app-mobile/root.tsx | 4 +-- packages/lib/BaseApplication.ts | 4 +-- .../shared/reduxSharedMiddleware.ts | 32 ++++++++++++++++--- packages/lib/reducer.ts | 15 +++++++-- 4 files changed, 43 insertions(+), 12 deletions(-) diff --git a/packages/app-mobile/root.tsx b/packages/app-mobile/root.tsx index 4b0404eaab3..75236847003 100644 --- a/packages/app-mobile/root.tsx +++ b/packages/app-mobile/root.tsx @@ -40,7 +40,7 @@ import { Provider as PaperProvider, MD3DarkTheme, MD3LightTheme } from 'react-na const { BackButtonService } = require('./services/back-button.js'); import NavService from '@joplin/lib/services/NavService'; import { createStore, applyMiddleware } from 'redux'; -const reduxSharedMiddleware = require('@joplin/lib/components/shared/reduxSharedMiddleware'); +import reduxSharedMiddleware from '@joplin/lib/components/shared/reduxSharedMiddleware'; const { shimInit } = require('./utils/shim-init-react.js'); const { AppNav } = require('./components/app-nav.js'); import Note from '@joplin/lib/models/Note'; @@ -149,7 +149,7 @@ const generalMiddleware = (store: any) => (next: any) => async (action: any) => const result = next(action); const newState = store.getState(); - await reduxSharedMiddleware(store, next, action); + await reduxSharedMiddleware(store, next, action, storeDispatch as any); if (action.type === 'NAV_GO') Keyboard.dismiss(); diff --git a/packages/lib/BaseApplication.ts b/packages/lib/BaseApplication.ts index af5c6b18065..6f00ff45b48 100644 --- a/packages/lib/BaseApplication.ts +++ b/packages/lib/BaseApplication.ts @@ -24,7 +24,7 @@ import { splitCommandString } from '@joplin/utils'; import { reg } from './registry'; import time from './time'; import BaseSyncTarget from './BaseSyncTarget'; -const reduxSharedMiddleware = require('./components/shared/reduxSharedMiddleware'); +import reduxSharedMiddleware from './components/shared/reduxSharedMiddleware'; const os = require('os'); import dns = require('dns'); import fs = require('fs-extra'); @@ -423,7 +423,7 @@ export default class BaseApplication { let refreshNotesUseSelectedNoteId = false; let refreshNotesHash = ''; - await reduxSharedMiddleware(store, next, action); + await reduxSharedMiddleware(store, next, action, ((action: any) => { this.dispatch(action); }) as any); const newState = store.getState() as State; if (this.hasGui() && ['NOTE_UPDATE_ONE', 'NOTE_DELETE', 'FOLDER_UPDATE_ONE', 'FOLDER_DELETE'].indexOf(action.type) >= 0) { diff --git a/packages/lib/components/shared/reduxSharedMiddleware.ts b/packages/lib/components/shared/reduxSharedMiddleware.ts index 1cb73fabe19..9eac5216556 100644 --- a/packages/lib/components/shared/reduxSharedMiddleware.ts +++ b/packages/lib/components/shared/reduxSharedMiddleware.ts @@ -7,13 +7,19 @@ import ResourceFetcher from '../../services/ResourceFetcher'; import DecryptionWorker from '../../services/DecryptionWorker'; import eventManager from '../../eventManager'; import BaseItem from '../../models/BaseItem'; +import shim from '../../shim'; +import { Dispatch } from 'redux'; +import { State } from '../../reducer'; -const reduxSharedMiddleware = async function(store: any, _next: any, action: any) { - const newState = store.getState(); +let sortNoteListTimeout: any = null; + +export default async (store: any, _next: any, action: any, dispatch: Dispatch) => { + const newState: State = store.getState(); eventManager.appStateEmit(newState); let refreshTags = false; + let sortNoteList = false; if (action.type === 'FOLDER_SET_COLLAPSED' || action.type === 'FOLDER_TOGGLE') { Setting.setValue('collapsedFolderIds', newState.collapsedFolderIds); @@ -57,6 +63,9 @@ const reduxSharedMiddleware = async function(store: any, _next: any, action: any refreshTags = true; } + if (action.type === 'NOTE_UPDATE_ONE') { + sortNoteList = true; + } if (action.type === 'NOTE_SELECT' || action.type === 'NAV_BACK') { const noteIds = newState.provisionalNoteIds.slice(); @@ -98,6 +107,22 @@ const reduxSharedMiddleware = async function(store: any, _next: any, action: any }); } + if (sortNoteList) { + if (sortNoteListTimeout) shim.clearTimeout(sortNoteListTimeout); + sortNoteListTimeout = null; + + // We sort the note lists with two seconds debounce because doing can be + // very slow and would have to be done every time a note is added. + if (Date.now() - newState.noteListLastSortTime > 10000) { + dispatch({ type: 'NOTE_SORT' }); + } else { + sortNoteListTimeout = shim.setTimeout(() => { + sortNoteListTimeout = null; + dispatch({ type: 'NOTE_SORT' }); + }, 2000); + } + } + if (action.type.startsWith('SHARE_')) { const serialized = JSON.stringify(newState.shareService); Setting.setValue('sync.shareCache', serialized); @@ -116,6 +141,3 @@ const reduxSharedMiddleware = async function(store: any, _next: any, action: any } } }; - -module.exports = reduxSharedMiddleware; - diff --git a/packages/lib/reducer.ts b/packages/lib/reducer.ts index 0ba81e05cd3..ecc6f705b62 100644 --- a/packages/lib/reducer.ts +++ b/packages/lib/reducer.ts @@ -102,6 +102,7 @@ export interface State { needApiAuth: boolean; profileConfig: ProfileConfig; noteListRendererIds: string[]; + noteListLastSortTime: number; // Extra reducer keys go here: pluginService: PluginServiceState; @@ -176,6 +177,7 @@ export const defaultState: State = { needApiAuth: false, profileConfig: null, noteListRendererIds: getListRendererIds(), + noteListLastSortTime: 0, pluginService: pluginServiceDefaultState, shareService: shareServiceDefaultState, @@ -850,6 +852,7 @@ const reducer = produce((draft: Draft = defaultState, action: any) => { case 'NOTE_UPDATE_ALL': draft.notes = action.notes; draft.notesSource = action.notesSource; + draft.noteListLastSortTime = Date.now(); // Notes are already sorted when they are set this way. updateSelectedNotesFromExistingNotes(draft); break; @@ -869,7 +872,7 @@ const reducer = produce((draft: Draft = defaultState, action: any) => { let movedNotePreviousIndex = 0; let noteFolderHasChanged = false; - let newNotes = draft.notes.slice(); + const newNotes = draft.notes.slice(); let found = false; for (let i = 0; i < newNotes.length; i++) { const n = newNotes[i]; @@ -909,8 +912,6 @@ const reducer = produce((draft: Draft = defaultState, action: any) => { } } - // newNotes = Note.sortNotes(newNotes, draft.notesOrder, draft.settings.uncompletedTodosOnTop); - newNotes = Note.sortNotes(newNotes, stateUtils.notesOrder(draft.settings), draft.settings.uncompletedTodosOnTop); draft.notes = newNotes; if (noteFolderHasChanged) { @@ -953,6 +954,14 @@ const reducer = produce((draft: Draft = defaultState, action: any) => { } break; + case 'NOTE_SORT': + + { + draft.notes = Note.sortNotes(draft.notes, stateUtils.notesOrder(draft.settings), draft.settings.uncompletedTodosOnTop); + draft.noteListLastSortTime = Date.now(); + } + break; + case 'NOTE_IS_INSERTING_NOTES': if (draft.isInsertingNotes !== action.value) {