From 7bf049a1f0994546291b0972cdeb85ff077309f5 Mon Sep 17 00:00:00 2001 From: Anoop M D Date: Sun, 15 Oct 2023 15:59:57 +0530 Subject: [PATCH] feat: use electron-store for saving preferences --- .../Preferences/Font/StyledWrapper.js | 20 ---- .../src/components/Preferences/Font/index.js | 60 ++++++++---- .../components/Preferences/General/index.js | 51 ++++++---- .../src/components/Preferences/index.js | 6 +- .../RequestPane/GraphQLVariables/index.js | 8 +- .../RequestPane/RequestBody/index.js | 7 +- .../components/RequestPane/Script/index.js | 9 +- .../src/components/RequestPane/Tests/index.js | 7 +- .../ResponsePane/QueryResult/index.js | 18 +++- .../GenerateCodeItem/CodeView/index.js | 15 ++- packages/bruno-app/src/pages/_app.js | 9 +- packages/bruno-app/src/providers/App/index.js | 4 +- ...eCollectionTreeSync.js => useIpcEvents.js} | 93 ++++++++++--------- .../src/providers/Preferences/index.js | 77 --------------- .../src/providers/ReduxStore/slices/app.js | 34 ++++++- .../ReduxStore/slices/collections/index.js | 1 - packages/bruno-electron/src/index.js | 6 +- packages/bruno-electron/src/ipc/collection.js | 22 +---- .../bruno-electron/src/ipc/preferences.js | 35 +++++++ .../bruno-electron/src/store/preferences.js | 75 +++++++++++---- .../bruno-electron/src/store/window-state.js | 1 - 21 files changed, 295 insertions(+), 263 deletions(-) rename packages/bruno-app/src/providers/App/{useCollectionTreeSync.js => useIpcEvents.js} (57%) delete mode 100644 packages/bruno-app/src/providers/Preferences/index.js create mode 100644 packages/bruno-electron/src/ipc/preferences.js diff --git a/packages/bruno-app/src/components/Preferences/Font/StyledWrapper.js b/packages/bruno-app/src/components/Preferences/Font/StyledWrapper.js index 5bd4e469..d45eda5b 100644 --- a/packages/bruno-app/src/components/Preferences/Font/StyledWrapper.js +++ b/packages/bruno-app/src/components/Preferences/Font/StyledWrapper.js @@ -2,26 +2,6 @@ import styled from 'styled-components'; const StyledWrapper = styled.div` color: ${(props) => props.theme.text}; - div.input-container { - background-color: ${(props) => props.theme.modal.input.bg}; - height: 2.3rem; - } - div.input-container { - border: solid 1px ${(props) => props.theme.modal.input.border}; - border-top-right-radius: 3px; - border-bottom-right-radius: 3px; - - input { - background-color: ${(props) => props.theme.modal.input.bg}; - outline: none; - box-shadow: none; - - &:focus { - outline: none !important; - box-shadow: none !important; - } - } - } `; export default StyledWrapper; diff --git a/packages/bruno-app/src/components/Preferences/Font/index.js b/packages/bruno-app/src/components/Preferences/Font/index.js index 4505b595..bae23e72 100644 --- a/packages/bruno-app/src/components/Preferences/Font/index.js +++ b/packages/bruno-app/src/components/Preferences/Font/index.js @@ -1,32 +1,52 @@ import React, { useState } from 'react'; -import { usePreferences } from 'providers/Preferences'; +import get from 'lodash/get'; +import { useSelector, useDispatch } from 'react-redux'; +import { savePreferences } from 'providers/ReduxStore/slices/app'; import StyledWrapper from './StyledWrapper'; -const Font = () => { - const { preferences, setPreferences } = usePreferences(); +const Font = ({ close }) => { + const dispatch = useDispatch(); + const preferences = useSelector((state) => state.app.preferences); - const [codeFont, setCodeFont] = useState(preferences.codeFont); + const [codeFont, setCodeFont] = useState(get(preferences, 'font.codeFont', 'default')); const handleInputChange = (event) => { - const updatedPreferences = { - ...preferences, - codeFont: event.target.value - }; - - setPreferences(updatedPreferences) - .then(() => { - setCodeFont(event.target.value); - }) - .catch((err) => { - console.error(err); - }); + setCodeFont(event.target.value); }; - return ( + const handleSave = () => { + dispatch( + savePreferences({ + ...preferences, + font: { + codeFont + } + }) + ).then(() => { + close(); + }); + }; + + return ( -

Font in code area

-
- + +
+ +
+ +
+
); diff --git a/packages/bruno-app/src/components/Preferences/General/index.js b/packages/bruno-app/src/components/Preferences/General/index.js index 637c483e..a622dd6b 100644 --- a/packages/bruno-app/src/components/Preferences/General/index.js +++ b/packages/bruno-app/src/components/Preferences/General/index.js @@ -1,35 +1,46 @@ import React, { useState } from 'react'; -import { usePreferences } from 'providers/Preferences'; +import { useSelector, useDispatch } from 'react-redux'; +import { savePreferences } from 'providers/ReduxStore/slices/app'; import StyledWrapper from './StyledWrapper'; -const General = () => { - const { preferences, setPreferences } = usePreferences(); +const General = ({ close }) => { + const preferences = useSelector((state) => state.app.preferences); + const dispatch = useDispatch(); const [sslVerification, setSslVerification] = useState(preferences.request.sslVerification); - const handleCheckboxChange = () => { - const updatedPreferences = { - ...preferences, - request: { - ...preferences.request, - sslVerification: !sslVerification - } - }; - - setPreferences(updatedPreferences) - .then(() => { - setSslVerification(!sslVerification); + const handleSave = () => { + dispatch( + savePreferences({ + ...preferences, + request: { + sslVerification + } }) - .catch((err) => { - console.error(err); - }); + ).then(() => { + close(); + }); }; return (
- - SSL Certificate Verification + setSslVerification(!sslVerification)} + className="mr-3 mousetrap" + /> + +
+ +
+
); diff --git a/packages/bruno-app/src/components/Preferences/index.js b/packages/bruno-app/src/components/Preferences/index.js index a48f5e66..217b3c57 100644 --- a/packages/bruno-app/src/components/Preferences/index.js +++ b/packages/bruno-app/src/components/Preferences/index.js @@ -19,11 +19,11 @@ const Preferences = ({ onClose }) => { const getTabPanel = (tab) => { switch (tab) { case 'general': { - return ; + return ; } case 'theme': { - return ; + return ; } case 'support': { @@ -31,7 +31,7 @@ const Preferences = ({ onClose }) => { } case 'font': { - return ; + return ; } } }; diff --git a/packages/bruno-app/src/components/RequestPane/GraphQLVariables/index.js b/packages/bruno-app/src/components/RequestPane/GraphQLVariables/index.js index 0e213442..0d913d97 100644 --- a/packages/bruno-app/src/components/RequestPane/GraphQLVariables/index.js +++ b/packages/bruno-app/src/components/RequestPane/GraphQLVariables/index.js @@ -1,17 +1,17 @@ import React from 'react'; -import { useDispatch } from 'react-redux'; +import get from 'lodash/get'; +import { useDispatch, useSelector } from 'react-redux'; import CodeEditor from 'components/CodeEditor'; import { updateRequestGraphqlVariables } from 'providers/ReduxStore/slices/collections'; import { sendRequest, saveRequest } from 'providers/ReduxStore/slices/collections/actions'; import { useTheme } from 'providers/Theme'; -import { usePreferences } from 'providers/Preferences'; import StyledWrapper from './StyledWrapper'; const GraphQLVariables = ({ variables, item, collection }) => { const dispatch = useDispatch(); const { storedTheme } = useTheme(); - const { preferences } = usePreferences(); + const preferences = useSelector((state) => state.app.preferences); const onEdit = (value) => { dispatch( @@ -32,7 +32,7 @@ const GraphQLVariables = ({ variables, item, collection }) => { collection={collection} value={variables || ''} theme={storedTheme} - font={preferences.codeFont} + font={get(preferences, 'font.codeFont', 'default')} onEdit={onEdit} mode="javascript" onRun={onRun} diff --git a/packages/bruno-app/src/components/RequestPane/RequestBody/index.js b/packages/bruno-app/src/components/RequestPane/RequestBody/index.js index 70ab1e84..9daaf37f 100644 --- a/packages/bruno-app/src/components/RequestPane/RequestBody/index.js +++ b/packages/bruno-app/src/components/RequestPane/RequestBody/index.js @@ -3,9 +3,8 @@ import get from 'lodash/get'; import CodeEditor from 'components/CodeEditor'; import FormUrlEncodedParams from 'components/RequestPane/FormUrlEncodedParams'; import MultipartFormParams from 'components/RequestPane/MultipartFormParams'; -import { useDispatch } from 'react-redux'; +import { useDispatch, useSelector } from 'react-redux'; import { useTheme } from 'providers/Theme'; -import { usePreferences } from 'providers/Preferences'; import { updateRequestBody } from 'providers/ReduxStore/slices/collections'; import { sendRequest, saveRequest } from 'providers/ReduxStore/slices/collections/actions'; import StyledWrapper from './StyledWrapper'; @@ -15,7 +14,7 @@ const RequestBody = ({ item, collection }) => { const body = item.draft ? get(item, 'draft.request.body') : get(item, 'request.body'); const bodyMode = item.draft ? get(item, 'draft.request.body.mode') : get(item, 'request.body.mode'); const { storedTheme } = useTheme(); - const { preferences } = usePreferences(); + const preferences = useSelector((state) => state.app.preferences); const onEdit = (value) => { dispatch( @@ -50,7 +49,7 @@ const RequestBody = ({ item, collection }) => { { @@ -14,7 +13,7 @@ const Script = ({ item, collection }) => { const responseScript = item.draft ? get(item, 'draft.request.script.res') : get(item, 'request.script.res'); const { storedTheme } = useTheme(); - const { preferences } = usePreferences(); + const preferences = useSelector((state) => state.app.preferences); const onRequestScriptEdit = (value) => { dispatch( @@ -47,7 +46,7 @@ const Script = ({ item, collection }) => { collection={collection} value={requestScript || ''} theme={storedTheme} - font={preferences.codeFont} + font={get(preferences, 'font.codeFont', 'default')} onEdit={onRequestScriptEdit} mode="javascript" onRun={onRun} @@ -60,7 +59,7 @@ const Script = ({ item, collection }) => { collection={collection} value={responseScript || ''} theme={storedTheme} - font={preferences.codeFont} + font={get(preferences, 'font.codeFont', 'default')} onEdit={onResponseScriptEdit} mode="javascript" onRun={onRun} diff --git a/packages/bruno-app/src/components/RequestPane/Tests/index.js b/packages/bruno-app/src/components/RequestPane/Tests/index.js index dce6547f..66645509 100644 --- a/packages/bruno-app/src/components/RequestPane/Tests/index.js +++ b/packages/bruno-app/src/components/RequestPane/Tests/index.js @@ -1,11 +1,10 @@ import React from 'react'; import get from 'lodash/get'; -import { useDispatch } from 'react-redux'; +import { useDispatch, useSelector } from 'react-redux'; import CodeEditor from 'components/CodeEditor'; import { updateRequestTests } from 'providers/ReduxStore/slices/collections'; import { sendRequest, saveRequest } from 'providers/ReduxStore/slices/collections/actions'; import { useTheme } from 'providers/Theme'; -import { usePreferences } from 'providers/Preferences'; import StyledWrapper from './StyledWrapper'; const Tests = ({ item, collection }) => { @@ -13,7 +12,7 @@ const Tests = ({ item, collection }) => { const tests = item.draft ? get(item, 'draft.request.tests') : get(item, 'request.tests'); const { storedTheme } = useTheme(); - const { preferences } = usePreferences(); + const preferences = useSelector((state) => state.app.preferences); const onEdit = (value) => { dispatch( @@ -34,7 +33,7 @@ const Tests = ({ item, collection }) => { collection={collection} value={tests || ''} theme={storedTheme} - font={preferences.codeFont} + font={get(preferences, 'font.codeFont', 'default')} onEdit={onEdit} mode="javascript" onRun={onRun} diff --git a/packages/bruno-app/src/components/ResponsePane/QueryResult/index.js b/packages/bruno-app/src/components/ResponsePane/QueryResult/index.js index fc271e74..bb29abd3 100644 --- a/packages/bruno-app/src/components/ResponsePane/QueryResult/index.js +++ b/packages/bruno-app/src/components/ResponsePane/QueryResult/index.js @@ -1,8 +1,8 @@ import React from 'react'; +import get from 'lodash/get'; import CodeEditor from 'components/CodeEditor'; import { useTheme } from 'providers/Theme'; -import { usePreferences } from 'providers/Preferences'; -import { useDispatch } from 'react-redux'; +import { useDispatch, useSelector } from 'react-redux'; import { sendRequest } from 'providers/ReduxStore/slices/collections/actions'; import classnames from 'classnames'; import { getContentType, safeStringifyJSON, safeParseXML } from 'utils/common'; @@ -14,7 +14,7 @@ import { useMemo } from 'react'; const QueryResult = ({ item, collection, data, width, disableRunEventListener, headers, error }) => { const { storedTheme } = useTheme(); - const { preferences } = usePreferences(); + const preferences = useSelector((state) => state.app.preferences); const [tab, setTab] = useState('preview'); const dispatch = useDispatch(); const contentType = getContentType(headers); @@ -113,7 +113,17 @@ const QueryResult = ({ item, collection, data, width, disableRunEventListener, h return image; } - return ; + return ( + + ); }, [tab, collection, storedTheme, onRun, value, mode]); return ( diff --git a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/GenerateCodeItem/CodeView/index.js b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/GenerateCodeItem/CodeView/index.js index f3223bab..64c229ae 100644 --- a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/GenerateCodeItem/CodeView/index.js +++ b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/GenerateCodeItem/CodeView/index.js @@ -1,12 +1,13 @@ import CodeEditor from 'components/CodeEditor/index'; +import get from 'lodash/get'; import { HTTPSnippet } from 'httpsnippet'; import { useTheme } from 'providers/Theme/index'; -import { usePreferences } from 'providers/Preferences/index'; import { buildHarRequest } from 'utils/codegenerator/har'; +import { useSelector } from 'react-redux'; const CodeView = ({ language, item }) => { const { storedTheme } = useTheme(); - const { preferences } = usePreferences(); + const preferences = useSelector((state) => state.app.preferences); const { target, client, language: lang } = language; let snippet = ''; @@ -17,7 +18,15 @@ const CodeView = ({ language, item }) => { snippet = 'Error generating code snippet'; } - return ; + return ( + + ); }; export default CodeView; diff --git a/packages/bruno-app/src/pages/_app.js b/packages/bruno-app/src/pages/_app.js index 67612904..0b2f9c3a 100644 --- a/packages/bruno-app/src/pages/_app.js +++ b/packages/bruno-app/src/pages/_app.js @@ -3,7 +3,6 @@ import { Provider } from 'react-redux'; import { AppProvider } from 'providers/App'; import { ToastProvider } from 'providers/Toaster'; import { HotkeysProvider } from 'providers/Hotkeys'; -import { PreferencesProvider } from 'providers/Preferences'; import ReduxStore from 'providers/ReduxStore'; import ThemeProvider from 'providers/Theme/index'; @@ -50,11 +49,9 @@ function MyApp({ Component, pageProps }) { - - - - - + + + diff --git a/packages/bruno-app/src/providers/App/index.js b/packages/bruno-app/src/providers/App/index.js index 041bf6e9..2fbd17e7 100644 --- a/packages/bruno-app/src/providers/App/index.js +++ b/packages/bruno-app/src/providers/App/index.js @@ -1,6 +1,6 @@ import React, { useEffect } from 'react'; import useTelemetry from './useTelemetry'; -import useCollectionTreeSync from './useCollectionTreeSync'; +import useIpcEvents from './useIpcEvents'; import useCollectionNextAction from './useCollectionNextAction'; import { useDispatch } from 'react-redux'; import { refreshScreenWidth } from 'providers/ReduxStore/slices/app'; @@ -10,7 +10,7 @@ export const AppContext = React.createContext(); export const AppProvider = (props) => { useTelemetry(); - useCollectionTreeSync(); + useIpcEvents(); useCollectionNextAction(); const dispatch = useDispatch(); diff --git a/packages/bruno-app/src/providers/App/useCollectionTreeSync.js b/packages/bruno-app/src/providers/App/useIpcEvents.js similarity index 57% rename from packages/bruno-app/src/providers/App/useCollectionTreeSync.js rename to packages/bruno-app/src/providers/App/useIpcEvents.js index caf057d5..8e87b1cf 100644 --- a/packages/bruno-app/src/providers/App/useCollectionTreeSync.js +++ b/packages/bruno-app/src/providers/App/useIpcEvents.js @@ -14,11 +14,12 @@ import { runFolderEvent, brunoConfigUpdateEvent } from 'providers/ReduxStore/slices/collections'; +import { updatePreferences } from 'providers/ReduxStore/slices/app'; import toast from 'react-hot-toast'; import { openCollectionEvent, collectionAddEnvFileEvent } from 'providers/ReduxStore/slices/collections/actions'; import { isElectron } from 'utils/common/platform'; -const useCollectionTreeSync = () => { +const useIpcEvents = () => { const dispatch = useDispatch(); useEffect(() => { @@ -28,10 +29,6 @@ const useCollectionTreeSync = () => { const { ipcRenderer } = window; - const _openCollection = (pathname, uid, brunoConfig) => { - dispatch(openCollectionEvent(uid, pathname, brunoConfig)); - }; - const _collectionTreeUpdated = (type, val) => { if (window.__IS_DEV__) { console.log(type); @@ -82,69 +79,73 @@ const useCollectionTreeSync = () => { } }; - const _collectionAlreadyOpened = (pathname) => { - toast.success('Collection is already opened'); - }; + ipcRenderer.invoke('renderer:ready'); + const removeCollectionTreeUpdateListener = ipcRenderer.on('main:collection-tree-updated', _collectionTreeUpdated); - const _displayError = (error) => { + const removeOpenCollectionListener = ipcRenderer.on('main:collection-opened', (pathname, uid, brunoConfig) => { + dispatch(openCollectionEvent(uid, pathname, brunoConfig)); + }); + + const removeCollectionAlreadyOpenedListener = ipcRenderer.on('main:collection-already-opened', (pathname) => { + toast.success('Collection is already opened'); + }); + + const removeDisplayErrorListener = ipcRenderer.on('main:display-error', (error) => { if (typeof error === 'string') { return toast.error(error || 'Something went wrong!'); } if (typeof message === 'object') { return toast.error(error.message || 'Something went wrong!'); } - }; + }); - const _scriptEnvironmentUpdate = (val) => { + const removeScriptEnvUpdateListener = ipcRenderer.on('main:script-environment-update', (val) => { dispatch(scriptEnvironmentUpdateEvent(val)); - }; + }); - const _processEnvUpdate = (val) => { - dispatch(processEnvUpdateEvent(val)); - }; - - const _collectionRenamed = (val) => { + const removeCollectionRenamedListener = ipcRenderer.on('main:collection-renamed', (val) => { dispatch(collectionRenamedEvent(val)); - }; + }); - const _runFolderEvent = (val) => { + const removeRunFolderEventListener = ipcRenderer.on('main:run-folder-event', (val) => { dispatch(runFolderEvent(val)); - }; + }); - const _runRequestEvent = (val) => { + const removeRunRequestEventListener = ipcRenderer.on('main:run-request-event', (val) => { dispatch(runRequestEvent(val)); - }; + }); - ipcRenderer.invoke('renderer:ready'); + const removeProcessEnvUpdatesListener = ipcRenderer.on('main:process-env-update', (val) => { + dispatch(processEnvUpdateEvent(val)); + }); - const removeListener1 = ipcRenderer.on('main:collection-opened', _openCollection); - const removeListener2 = ipcRenderer.on('main:collection-tree-updated', _collectionTreeUpdated); - const removeListener3 = ipcRenderer.on('main:collection-already-opened', _collectionAlreadyOpened); - const removeListener4 = ipcRenderer.on('main:display-error', _displayError); - const removeListener5 = ipcRenderer.on('main:script-environment-update', _scriptEnvironmentUpdate); - const removeListener6 = ipcRenderer.on('main:collection-renamed', _collectionRenamed); - const removeListener7 = ipcRenderer.on('main:run-folder-event', _runFolderEvent); - const removeListener8 = ipcRenderer.on('main:run-request-event', _runRequestEvent); - const removeListener9 = ipcRenderer.on('main:process-env-update', _processEnvUpdate); - const removeListener10 = ipcRenderer.on('main:console-log', (val) => { + const removeConsoleLogListener = ipcRenderer.on('main:console-log', (val) => { console[val.type](...val.args); }); - const removeListener11 = ipcRenderer.on('main:bruno-config-update', (val) => dispatch(brunoConfigUpdateEvent(val))); + + const removeConfigUpdatesListener = ipcRenderer.on('main:bruno-config-update', (val) => + dispatch(brunoConfigUpdateEvent(val)) + ); + + const removePreferencesUpdatesListener = ipcRenderer.on('main:load-preferences', (val) => { + dispatch(updatePreferences(val)); + }); return () => { - removeListener1(); - removeListener2(); - removeListener3(); - removeListener4(); - removeListener5(); - removeListener6(); - removeListener7(); - removeListener8(); - removeListener9(); - removeListener10(); - removeListener11(); + removeCollectionTreeUpdateListener(); + removeOpenCollectionListener(); + removeCollectionAlreadyOpenedListener(); + removeDisplayErrorListener(); + removeScriptEnvUpdateListener(); + removeCollectionRenamedListener(); + removeRunFolderEventListener(); + removeRunRequestEventListener(); + removeProcessEnvUpdatesListener(); + removeConsoleLogListener(); + removeConfigUpdatesListener(); + removePreferencesUpdatesListener(); }; }, [isElectron]); }; -export default useCollectionTreeSync; +export default useIpcEvents; diff --git a/packages/bruno-app/src/providers/Preferences/index.js b/packages/bruno-app/src/providers/Preferences/index.js deleted file mode 100644 index 9b034500..00000000 --- a/packages/bruno-app/src/providers/Preferences/index.js +++ /dev/null @@ -1,77 +0,0 @@ -/** - * Preferences Provider - * - * This provider is responsible for managing the user's preferences in the app. - * The preferences are stored in the browser local storage. - * - * On start, an IPC event is published to the main process to set the preferences in the electron process. - */ - -import { useEffect, createContext, useContext } from 'react'; -import * as Yup from 'yup'; -import useLocalStorage from 'hooks/useLocalStorage/index'; -import toast from 'react-hot-toast'; - -const defaultPreferences = { - request: { - sslVerification: true - } -}; - -const preferencesSchema = Yup.object().shape({ - request: Yup.object().shape({ - sslVerification: Yup.boolean() - }) -}); - -export const PreferencesContext = createContext(); -export const PreferencesProvider = (props) => { - const [preferences, setPreferences] = useLocalStorage('bruno.preferences', defaultPreferences); - const { ipcRenderer } = window; - - useEffect(() => { - ipcRenderer.invoke('renderer:set-preferences', preferences).catch((err) => { - toast.error(err.message || 'Preferences sync error'); - }); - }, [preferences, toast]); - - const validatedSetPreferences = (newPreferences) => { - return new Promise((resolve, reject) => { - preferencesSchema - .validate(newPreferences, { abortEarly: true }) - .then((validatedPreferences) => { - setPreferences(validatedPreferences); - resolve(validatedPreferences); - }) - .catch((error) => { - let errMsg = error.message || 'Preferences validation error'; - toast.error(errMsg); - reject(error); - }); - }); - }; - - // todo: setPreferences must validate the preferences object against a schema - const value = { - preferences, - setPreferences: validatedSetPreferences - }; - - return ( - - <>{props.children} - - ); -}; - -export const usePreferences = () => { - const context = useContext(PreferencesContext); - - if (context === undefined) { - throw new Error(`usePreferences must be used within a PreferencesProvider`); - } - - return context; -}; - -export default PreferencesProvider; diff --git a/packages/bruno-app/src/providers/ReduxStore/slices/app.js b/packages/bruno-app/src/providers/ReduxStore/slices/app.js index f1e9b911..c3a3aa5a 100644 --- a/packages/bruno-app/src/providers/ReduxStore/slices/app.js +++ b/packages/bruno-app/src/providers/ReduxStore/slices/app.js @@ -1,11 +1,20 @@ import { createSlice } from '@reduxjs/toolkit'; +import toast from 'react-hot-toast'; const initialState = { isDragging: false, idbConnectionReady: false, leftSidebarWidth: 222, screenWidth: 500, - showHomePage: false + showHomePage: false, + preferences: { + request: { + sslVerification: true + }, + font: { + codeFont: 'default' + } + } }; export const appSlice = createSlice({ @@ -29,6 +38,9 @@ export const appSlice = createSlice({ }, hideHomePage: (state) => { state.showHomePage = false; + }, + updatePreferences: (state, action) => { + state.preferences = action.payload; } } }); @@ -39,7 +51,25 @@ export const { updateLeftSidebarWidth, updateIsDragging, showHomePage, - hideHomePage + hideHomePage, + updatePreferences } = appSlice.actions; +export const savePreferences = (preferences) => (dispatch, getState) => { + return new Promise((resolve, reject) => { + const { ipcRenderer } = window; + + ipcRenderer + .invoke('renderer:save-preferences', preferences) + .then(() => toast.success('Preferences saved successfully')) + .then(() => dispatch(updatePreferences(preferences))) + .then(resolve) + .catch((err) => { + toast.error('An error occurred while saving preferences'); + console.error(err); + reject(err); + }); + }); +}; + export default appSlice.reducer; diff --git a/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js b/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js index c5cb71f8..c60b3e68 100644 --- a/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js +++ b/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js @@ -1057,7 +1057,6 @@ export const collectionsSlice = createSlice({ if (collection) { collection.root = file.data; } - console.log('collectionAddFileEvent', file); return; } diff --git a/packages/bruno-electron/src/index.js b/packages/bruno-electron/src/index.js index 1acaa7ad..2199122d 100644 --- a/packages/bruno-electron/src/index.js +++ b/packages/bruno-electron/src/index.js @@ -8,6 +8,7 @@ const menuTemplate = require('./app/menu-template'); const LastOpenedCollections = require('./store/last-opened-collections'); const registerNetworkIpc = require('./ipc/network'); const registerCollectionsIpc = require('./ipc/collection'); +const registerPreferencesIpc = require('./ipc/preferences'); const Watcher = require('./app/watcher'); const { loadWindowState, saveWindowState } = require('./utils/window'); @@ -39,8 +40,8 @@ app.on('ready', async () => { y, width, height, - minWidth:1000, - minHeight:640, + minWidth: 1000, + minHeight: 640, webPreferences: { nodeIntegration: true, contextIsolation: true, @@ -76,6 +77,7 @@ app.on('ready', async () => { // register all ipc handlers registerNetworkIpc(mainWindow, watcher, lastOpenedCollections); registerCollectionsIpc(mainWindow, watcher, lastOpenedCollections); + registerPreferencesIpc(mainWindow, watcher, lastOpenedCollections); }); // Quit the app once all windows are closed diff --git a/packages/bruno-electron/src/ipc/collection.js b/packages/bruno-electron/src/ipc/collection.js index 944a04f0..3b121200 100644 --- a/packages/bruno-electron/src/ipc/collection.js +++ b/packages/bruno-electron/src/ipc/collection.js @@ -15,10 +15,9 @@ const { sanitizeDirectoryName } = require('../utils/filesystem'); const { stringifyJson } = require('../utils/common'); -const { openCollectionDialog, openCollection } = require('../app/collections'); +const { openCollectionDialog } = require('../app/collections'); const { generateUidBasedOnHash } = require('../utils/common'); const { moveRequestUid, deleteRequestUid } = require('../cache/requestUids'); -const { setPreferences } = require('../store/preferences'); const EnvironmentSecretsStore = require('../store/env-secrets'); const environmentSecretsStore = new EnvironmentSecretsStore(); @@ -469,25 +468,6 @@ const registerRendererEventHandlers = (mainWindow, watcher, lastOpenedCollection } }); - ipcMain.handle('renderer:ready', async (event) => { - // reload last opened collections - const lastOpened = lastOpenedCollections.getAll(); - - if (lastOpened && lastOpened.length) { - for (let collectionPath of lastOpened) { - if (isDirectory(collectionPath)) { - openCollection(mainWindow, watcher, collectionPath, { - dontSendDisplayErrors: true - }); - } - } - } - }); - - ipcMain.handle('renderer:set-preferences', async (event, preferences) => { - setPreferences(preferences); - }); - ipcMain.handle('renderer:update-bruno-config', async (event, brunoConfig, collectionPath, collectionUid) => { try { const brunoConfigPath = path.join(collectionPath, 'bruno.json'); diff --git a/packages/bruno-electron/src/ipc/preferences.js b/packages/bruno-electron/src/ipc/preferences.js new file mode 100644 index 00000000..f93ec5e6 --- /dev/null +++ b/packages/bruno-electron/src/ipc/preferences.js @@ -0,0 +1,35 @@ +const { ipcMain } = require('electron'); +const { getPreferences, savePreferences } = require('../store/preferences'); +const { isDirectory } = require('../utils/filesystem'); +const { openCollection } = require('../app/collections'); + +const registerPreferencesIpc = (mainWindow, watcher, lastOpenedCollections) => { + ipcMain.handle('renderer:ready', async (event) => { + // load preferences + const preferences = getPreferences(); + mainWindow.webContents.send('main:load-preferences', preferences); + + // reload last opened collections + const lastOpened = lastOpenedCollections.getAll(); + + if (lastOpened && lastOpened.length) { + for (let collectionPath of lastOpened) { + if (isDirectory(collectionPath)) { + openCollection(mainWindow, watcher, collectionPath, { + dontSendDisplayErrors: true + }); + } + } + } + }); + + ipcMain.handle('renderer:save-preferences', async (event, preferences) => { + try { + await savePreferences(preferences); + } catch (error) { + return Promise.reject(error); + } + }); +}; + +module.exports = registerPreferencesIpc; diff --git a/packages/bruno-electron/src/store/preferences.js b/packages/bruno-electron/src/store/preferences.js index f1b86b0f..869895cd 100644 --- a/packages/bruno-electron/src/store/preferences.js +++ b/packages/bruno-electron/src/store/preferences.js @@ -1,26 +1,65 @@ -/** - * The preferences are stored in the browser local storage. - * When the app is started, an IPC message is published from the renderer process to set the preferences. - * The electron process uses this module to get the preferences. - * - * { - * request: { - * sslVerification: boolean - * } - * } - */ +const Yup = require('yup'); +const Store = require('electron-store'); -let preferences = {}; - -const getPreferences = () => { - return preferences; +const defaultPreferences = { + request: { + sslVerification: true + }, + font: { + codeFont: 'default' + } }; -const setPreferences = (newPreferences) => { - preferences = newPreferences; +const preferencesSchema = Yup.object().shape({ + request: Yup.object().shape({ + sslVerification: Yup.boolean() + }), + font: Yup.object().shape({ + codeFont: Yup.string().nullable() + }) +}); + +class PreferencesStore { + constructor() { + this.store = new Store({ + name: 'preferences', + clearInvalidConfig: true + }); + } + + getPreferences() { + return { + defaultPreferences, + ...this.store.get('preferences') + }; + } + + savePreferences(newPreferences) { + return this.store.set('preferences', newPreferences); + } +} + +const preferencesStore = new PreferencesStore(); + +const getPreferences = () => { + return preferencesStore.getPreferences(); +}; + +const savePreferences = async (newPreferences) => { + return new Promise((resolve, reject) => { + preferencesSchema + .validate(newPreferences, { abortEarly: true }) + .then((validatedPreferences) => { + preferencesStore.savePreferences(validatedPreferences); + resolve(); + }) + .catch((error) => { + reject(error); + }); + }); }; module.exports = { getPreferences, - setPreferences + savePreferences }; diff --git a/packages/bruno-electron/src/store/window-state.js b/packages/bruno-electron/src/store/window-state.js index bb0a61b6..90bf7b8c 100644 --- a/packages/bruno-electron/src/store/window-state.js +++ b/packages/bruno-electron/src/store/window-state.js @@ -1,4 +1,3 @@ -const _ = require('lodash'); const Store = require('electron-store'); const DEFAULT_WINDOW_WIDTH = 1280;