From d14b694b6c40ba128e41f979631cc2460114a826 Mon Sep 17 00:00:00 2001 From: Laurent Cozic Date: Sun, 24 Sep 2023 20:21:58 +0100 Subject: [PATCH] Desktop, Mobile: Resolves #8021: Remember whether "All notes", a notebook or a tag was opened when re-opening the app --- packages/app-desktop/app.ts | 34 ++++++++++++++++++++++--- packages/app-mobile/root.tsx | 13 ++++++++-- packages/lib/BaseApplication.ts | 8 ++++-- packages/lib/models/Setting.ts | 1 + packages/lib/reducer.ts | 45 +++++++++++++++++++++++++++++++++ 5 files changed, 93 insertions(+), 8 deletions(-) diff --git a/packages/app-desktop/app.ts b/packages/app-desktop/app.ts index 873087718..f0a46509a 100644 --- a/packages/app-desktop/app.ts +++ b/packages/app-desktop/app.ts @@ -67,6 +67,7 @@ import eventManager from '@joplin/lib/eventManager'; import path = require('path'); import { checkPreInstalledDefaultPlugins, installDefaultPlugins, setSettingsForDefaultPlugins } from '@joplin/lib/services/plugins/defaultPlugins/defaultPluginsUtils'; import userFetcher, { initializeUserFetcher } from '@joplin/lib/utils/userFetcher'; +import { parseNotesParent } from '@joplin/lib/reducer'; const pluginClasses = [ require('./plugins/GotoAnything').default, @@ -447,10 +448,35 @@ class Application extends BaseApplication { // items: masterKeys, // }); - this.store().dispatch({ - type: 'FOLDER_SELECT', - id: Setting.value('activeFolderId'), - }); + const getNotesParent = async () => { + let notesParent = parseNotesParent(Setting.value('notesParent'), Setting.value('activeFolderId')); + if (notesParent.type === 'Tag' && !(await Tag.load(notesParent.selectedItemId))) { + notesParent = { + type: 'Folder', + selectedItemId: Setting.value('activeFolderId'), + }; + } + return notesParent; + }; + + const notesParent = await getNotesParent(); + + if (notesParent.type === 'SmartFilter') { + this.store().dispatch({ + type: 'SMART_FILTER_SELECT', + id: notesParent.selectedItemId, + }); + } else if (notesParent.type === 'Tag') { + this.store().dispatch({ + type: 'TAG_SELECT', + id: notesParent.selectedItemId, + }); + } else { + this.store().dispatch({ + type: 'FOLDER_SELECT', + id: notesParent.selectedItemId, + }); + } this.store().dispatch({ type: 'FOLDER_SET_COLLAPSED_ALL', diff --git a/packages/app-mobile/root.tsx b/packages/app-mobile/root.tsx index a66920e21..88bfa56a4 100644 --- a/packages/app-mobile/root.tsx +++ b/packages/app-mobile/root.tsx @@ -16,7 +16,7 @@ import NoteScreen from './components/screens/Note'; import UpgradeSyncTargetScreen from './components/screens/UpgradeSyncTargetScreen'; import Setting, { Env } from '@joplin/lib/models/Setting'; import PoorManIntervals from '@joplin/lib/PoorManIntervals'; -import reducer from '@joplin/lib/reducer'; +import reducer, { NotesParent, parseNotesParent, serializeNotesParent } from '@joplin/lib/reducer'; import ShareExtension from './utils/ShareExtension'; import handleShared from './utils/shareHandler'; import uuid from '@joplin/lib/uuid'; @@ -199,6 +199,11 @@ const generalMiddleware = (store: any) => (next: any) => async (action: any) => if (action.type === 'NAV_GO' && action.routeName === 'Notes') { Setting.setValue('activeFolderId', newState.selectedFolderId); + const notesParent: NotesParent = { + type: action.smartFilterId ? 'SmartFilter' : 'Folder', + selectedItemId: action.smartFilterId ? action.smartFilterId : newState.selectedFolderId, + }; + Setting.setValue('notesParent', serializeNotesParent(notesParent)); } if (action.type === 'SYNC_GOT_ENCRYPTED_ITEM') { @@ -649,7 +654,11 @@ async function initialize(dispatch: Function) { ids: Setting.value('collapsedFolderIds'), }); - if (!folder) { + const notesParent = parseNotesParent(Setting.value('notesParent'), Setting.value('activeFolderId')); + + if (notesParent && notesParent.type === 'SmartFilter') { + dispatch(DEFAULT_ROUTE); + } else if (!folder) { dispatch(DEFAULT_ROUTE); } else { dispatch({ diff --git a/packages/lib/BaseApplication.ts b/packages/lib/BaseApplication.ts index 40d95a7e7..581a98a45 100644 --- a/packages/lib/BaseApplication.ts +++ b/packages/lib/BaseApplication.ts @@ -3,7 +3,7 @@ import Logger, { TargetType, LoggerWrapper } from '@joplin/utils/Logger'; import shim from './shim'; const { setupProxySettings } = require('./shim-init-node'); import BaseService from './services/BaseService'; -import reducer, { setStore } from './reducer'; +import reducer, { getNotesParent, serializeNotesParent, setStore, State } from './reducer'; import KeychainServiceDriver from './services/keychain/KeychainServiceDriver.node'; import KeychainServiceDriverDummy from './services/keychain/KeychainServiceDriver.dummy'; import { _, setLocale } from './locale'; @@ -544,7 +544,7 @@ export default class BaseApplication { let refreshNotesHash = ''; await reduxSharedMiddleware(store, next, action); - const newState = store.getState(); + const newState = store.getState() as State; if (this.hasGui() && ['NOTE_UPDATE_ONE', 'NOTE_DELETE', 'FOLDER_UPDATE_ONE', 'FOLDER_DELETE'].indexOf(action.type) >= 0) { if (!(await reg.syncTarget().syncStarted())) void reg.scheduleSync(15 * 1000, { syncSteps: ['update_remote', 'delete_remote'] }); @@ -573,6 +573,10 @@ export default class BaseApplication { } } + if (['HISTORY_BACKWARD', 'HISTORY_FORWARD', 'FOLDER_SELECT', 'TAG_SELECT', 'SMART_FILTER_SELECT', 'FOLDER_DELETE', 'FOLDER_AND_NOTE_SELECT'].includes(action.type) || (action.type === 'SEARCH_UPDATE' && newState.notesParentType === 'Folder')) { + Setting.setValue('notesParent', serializeNotesParent(getNotesParent(newState))); + } + if (this.hasGui() && (action.type === 'NOTE_IS_INSERTING_NOTES' && !action.value)) { refreshNotes = true; } diff --git a/packages/lib/models/Setting.ts b/packages/lib/models/Setting.ts index a47203b8f..72f32434d 100644 --- a/packages/lib/models/Setting.ts +++ b/packages/lib/models/Setting.ts @@ -771,6 +771,7 @@ class Setting extends BaseModel { // selected folder. It corresponds in general to the currently selected folder or // to the last folder that was selected. activeFolderId: { value: '', type: SettingItemType.String, public: false }, + notesParent: { value: '', type: SettingItemType.String, public: false }, richTextBannerDismissed: { value: false, type: SettingItemType.Bool, storage: SettingStorage.File, isGlobal: true, public: false }, diff --git a/packages/lib/reducer.ts b/packages/lib/reducer.ts index 85235102e..7207c779c 100644 --- a/packages/lib/reducer.ts +++ b/packages/lib/reducer.ts @@ -464,6 +464,51 @@ function defaultNotesParentType(draft: Draft, exclusion: string) { return newNotesParentType; } +export type NotesParentType = 'Folder' | 'Tag' | 'SmartFilter'; + +export interface NotesParent { + type: NotesParentType; + selectedItemId: string; +} + +export const serializeNotesParent = (n: NotesParent) => { + return JSON.stringify(n); +}; + +export const parseNotesParent = (s: string, activeFolderId: string): NotesParent => { + const defaultValue: NotesParent = { + type: 'Folder', + selectedItemId: activeFolderId, + }; + + if (!s) return defaultValue; + + try { + const parsed = JSON.parse(s); + return parsed; + } catch (error) { + return defaultValue; + } +}; + +export const getNotesParent = (state: State): NotesParent => { + let type = state.notesParentType as NotesParentType; + let selectedItemId = ''; + + if (type === 'Folder') { + selectedItemId = state.selectedFolderId; + } else if (type === 'Tag') { + selectedItemId = state.selectedTagId; + } else if (type === 'SmartFilter') { + selectedItemId = state.selectedSmartFilterId; + } else { + type = 'Folder'; + selectedItemId = state.selectedFolderId; + } + + return { type, selectedItemId }; +}; + function changeSelectedFolder(draft: Draft, action: any, options: any = null) { if (!options) options = {}; draft.selectedFolderId = 'folderId' in action ? action.folderId : action.id;