diff --git a/packages/bruno-app/src/providers/App/useIpcEvents.js b/packages/bruno-app/src/providers/App/useIpcEvents.js index f4a04030f..b885ad74d 100644 --- a/packages/bruno-app/src/providers/App/useIpcEvents.js +++ b/packages/bruno-app/src/providers/App/useIpcEvents.js @@ -19,7 +19,7 @@ import { runRequestEvent, scriptEnvironmentUpdateEvent } from 'providers/ReduxStore/slices/collections'; -import { collectionAddEnvFileEvent, openCollectionEvent } from 'providers/ReduxStore/slices/collections/actions'; +import { collectionAddEnvFileEvent, openCollectionEvent, hydrateCollectionsWithUiStateSnapshot } from 'providers/ReduxStore/slices/collections/actions'; import toast from 'react-hot-toast'; import { useDispatch } from 'react-redux'; import { isElectron } from 'utils/common/platform'; @@ -149,6 +149,10 @@ const useIpcEvents = () => { dispatch(updateCookies(val)); }); + const removeSnapshotHydrationListener = ipcRenderer.on('main:hydrate-app-with-ui-state-snapshot', (val) => { + dispatch(hydrateCollectionsWithUiStateSnapshot(val)); + }) + return () => { removeCollectionTreeUpdateListener(); removeOpenCollectionListener(); @@ -165,6 +169,7 @@ const useIpcEvents = () => { removePreferencesUpdatesListener(); removeCookieUpdateListener(); removeSystemProxyEnvUpdatesListener(); + removeSnapshotHydrationListener(); }; }, [isElectron]); }; diff --git a/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js b/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js index 777a194ab..e582b0f1f 100644 --- a/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js +++ b/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js @@ -44,6 +44,7 @@ import { parsePathParams, parseQueryParams, splitOnFirst } from 'utils/url/index import { sendCollectionOauth2Request as _sendCollectionOauth2Request } from 'utils/network/index'; import { name } from 'file-loader'; import slash from 'utils/common/slash'; +import { findCollectionByPathname, findEnvironmentInCollectionByName } from 'utils/collections/index'; export const renameCollection = (newName, collectionUid) => (dispatch, getState) => { const state = getState(); @@ -972,13 +973,15 @@ export const selectEnvironment = (environmentUid, collectionUid) => (dispatch, g const collectionCopy = cloneDeep(collection); if (environmentUid) { const environment = findEnvironmentInCollection(collectionCopy, environmentUid); - if (!environment) { + if (environment) { + ipcRenderer.invoke('renderer:update-ui-state-snapshot', { type: 'COLLECTION_ENVIRONMENT', data: { collectionPath: collection?.pathname, environmentName: environment?.name } }) + dispatch(_selectEnvironment({ environmentUid, collectionUid })); + resolve(); + } + else { return reject(new Error('Environment not found')); } } - - dispatch(_selectEnvironment({ environmentUid, collectionUid })); - resolve(); }); }; @@ -1141,3 +1144,33 @@ export const saveCollectionSecurityConfig = (collectionUid, securityConfig) => ( .catch(reject); }); }; + + +export const hydrateCollectionsWithUiStateSnapshot = (payload) => (dispatch, getState) => { + const collectionSnapshotData = payload; + return new Promise((resolve, reject) => { + const state = getState(); + try { + if(!collectionSnapshotData) resolve(); + const { pathname, selectedEnvironment } = collectionSnapshotData; + const collection = findCollectionByPathname(state.collections.collections, pathname); + const collectionCopy = cloneDeep(collection); + const collectionUid = collectionCopy?.uid; + + // update selected environment + if (selectedEnvironment) { + const environment = findEnvironmentInCollectionByName(collectionCopy, selectedEnvironment); + if (environment) { + dispatch(_selectEnvironment({ environmentUid: environment?.uid, collectionUid })); + } + } + + // todo: add any other redux state that you want to save + + resolve(); + } + catch(error) { + reject(error); + } + }); + }; \ No newline at end of file diff --git a/packages/bruno-app/src/utils/collections/index.js b/packages/bruno-app/src/utils/collections/index.js index ea8712be5..ddb98f531 100644 --- a/packages/bruno-app/src/utils/collections/index.js +++ b/packages/bruno-app/src/utils/collections/index.js @@ -132,6 +132,10 @@ export const findEnvironmentInCollection = (collection, envUid) => { return find(collection.environments, (e) => e.uid === envUid); }; +export const findEnvironmentInCollectionByName = (collection, name) => { + return find(collection.environments, (e) => e.name === name); +}; + export const moveCollectionItem = (collection, draggedItem, targetItem) => { let draggedItemParent = findParentItemInCollection(collection, draggedItem.uid); diff --git a/packages/bruno-electron/src/app/watcher.js b/packages/bruno-electron/src/app/watcher.js index b93b01a55..82d116d81 100644 --- a/packages/bruno-electron/src/app/watcher.js +++ b/packages/bruno-electron/src/app/watcher.js @@ -12,6 +12,7 @@ const { decryptString } = require('../utils/encryption'); const { setDotEnvVars } = require('../store/process-env'); const { setBrunoConfig } = require('../store/bruno-config'); const EnvironmentSecretsStore = require('../store/env-secrets'); +const UiStateSnapshot = require('../store/ui-state-snapshot'); const environmentSecretsStore = new EnvironmentSecretsStore(); @@ -421,6 +422,13 @@ const unlinkDir = (win, pathname, collectionUid, collectionPath) => { win.webContents.send('main:collection-tree-updated', 'unlinkDir', directory); }; +const onWatcherSetupComplete = (win, collectionPath) => { + const UiStateSnapshotStore = new UiStateSnapshot(); + const collectionsSnapshotState = UiStateSnapshotStore.getCollections(); + const collectionSnapshotState = collectionsSnapshotState?.find(c => c?.pathname == collectionPath); + win.webContents.send('main:hydrate-app-with-ui-state-snapshot', collectionSnapshotState); +}; + class Watcher { constructor() { this.watchers = {}; @@ -456,6 +464,7 @@ class Watcher { let startedNewWatcher = false; watcher + .on('ready', () => onWatcherSetupComplete(win, watchPath)) .on('add', (pathname) => add(win, pathname, collectionUid, watchPath)) .on('addDir', (pathname) => addDirectory(win, pathname, collectionUid, watchPath)) .on('change', (pathname) => change(win, pathname, collectionUid, watchPath)) diff --git a/packages/bruno-electron/src/ipc/collection.js b/packages/bruno-electron/src/ipc/collection.js index 950902ece..0aa2058c2 100644 --- a/packages/bruno-electron/src/ipc/collection.js +++ b/packages/bruno-electron/src/ipc/collection.js @@ -25,9 +25,11 @@ const { moveRequestUid, deleteRequestUid } = require('../cache/requestUids'); const { deleteCookiesForDomain, getDomainsWithCookies } = require('../utils/cookies'); const EnvironmentSecretsStore = require('../store/env-secrets'); const CollectionSecurityStore = require('../store/collection-security'); +const UiStateSnapshot = require('../store/ui-state-snapshot'); const environmentSecretsStore = new EnvironmentSecretsStore(); const collectionSecurityStore = new CollectionSecurityStore(); +const UiStateSnapshotStore = new UiStateSnapshot(); const envHasSecrets = (environment = {}) => { const secrets = _.filter(environment.variables, (v) => v.secret); @@ -695,6 +697,14 @@ const registerRendererEventHandlers = (mainWindow, watcher, lastOpenedCollection return Promise.reject(error); } }); + + ipcMain.handle('renderer:update-ui-state-snapshot', (event, { type, data }) => { + try { + UiStateSnapshotStore.update({ type, data }); + } catch (error) { + throw new Error(error.message); + } + }); }; const registerMainEventHandlers = (mainWindow, watcher, lastOpenedCollections) => { diff --git a/packages/bruno-electron/src/store/ui-state-snapshot.js b/packages/bruno-electron/src/store/ui-state-snapshot.js new file mode 100644 index 000000000..b3d1909b6 --- /dev/null +++ b/packages/bruno-electron/src/store/ui-state-snapshot.js @@ -0,0 +1,60 @@ +const Store = require('electron-store'); + +class UiStateSnapshot { + constructor() { + this.store = new Store({ + name: 'ui-state-snapshot', + clearInvalidConfig: true + }); + } + + getCollections() { + return this.store.get('collections') || []; + } + + saveCollections(collections) { + this.store.set('collections', collections); + } + + getCollectionByPathname({ pathname }) { + let collections = this.getCollections(); + + let collection = collections.find(c => c?.pathname === pathname); + if (!collection) { + collection = { pathname }; + collections.push(collection); + this.saveCollections(collections); + } + + return collection; + } + + setCollectionByPathname({ collection }) { + let collections = this.getCollections(); + + collections = collections.filter(c => c?.pathname !== collection.pathname); + collections.push({ ...collection }); + this.saveCollections(collections); + + return collection; + } + + updateCollectionEnvironment({ collectionPath, environmentName }) { + const collection = this.getCollectionByPathname({ pathname: collectionPath }); + collection.selectedEnvironment = environmentName; + this.setCollectionByPathname({ collection }); + } + + update({ type, data }) { + switch(type) { + case 'COLLECTION_ENVIRONMENT': + const { collectionPath, environmentName } = data; + this.updateCollectionEnvironment({ collectionPath, environmentName }); + break; + default: + break; + } + } +} + +module.exports = UiStateSnapshot;