mirror of
https://github.com/usebruno/bruno.git
synced 2024-12-23 07:09:01 +01:00
feat: use electron-store for saving preferences
This commit is contained in:
parent
363cbe75d2
commit
7bf049a1f0
@ -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;
|
||||
|
@ -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 (
|
||||
<StyledWrapper>
|
||||
<h2>Font in code area</h2>
|
||||
<div className='input-container'>
|
||||
<input type="text" onChange={handleInputChange} placeholder="Local font" defaultValue={codeFont} className="w-full px-3 py-1.5" />
|
||||
<label className="block font-medium">Code Editor Font</label>
|
||||
<div className="input-container">
|
||||
<input
|
||||
type="text"
|
||||
className="block textbox mt-2 w-full"
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
autoCapitalize="off"
|
||||
spellCheck="false"
|
||||
onChange={handleInputChange}
|
||||
defaultValue={codeFont}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mt-10">
|
||||
<button type="submit" className="submit btn btn-sm btn-secondary" onClick={handleSave}>
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</StyledWrapper>
|
||||
);
|
||||
|
@ -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 (
|
||||
<StyledWrapper>
|
||||
<div className="flex items-center mt-2">
|
||||
<input type="checkbox" checked={sslVerification} onChange={handleCheckboxChange} className="mr-3 mousetrap" />
|
||||
SSL Certificate Verification
|
||||
<input
|
||||
id="ssl-verification"
|
||||
type="checkbox"
|
||||
checked={sslVerification}
|
||||
onChange={() => setSslVerification(!sslVerification)}
|
||||
className="mr-3 mousetrap"
|
||||
/>
|
||||
<label htmlFor="ssl-verification" className="select-none">
|
||||
SSL Certificate Verification
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div className="mt-10">
|
||||
<button type="submit" className="submit btn btn-sm btn-secondary" onClick={handleSave}>
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</StyledWrapper>
|
||||
);
|
||||
|
@ -19,11 +19,11 @@ const Preferences = ({ onClose }) => {
|
||||
const getTabPanel = (tab) => {
|
||||
switch (tab) {
|
||||
case 'general': {
|
||||
return <General />;
|
||||
return <General close={onClose} />;
|
||||
}
|
||||
|
||||
case 'theme': {
|
||||
return <Theme />;
|
||||
return <Theme close={onClose} />;
|
||||
}
|
||||
|
||||
case 'support': {
|
||||
@ -31,7 +31,7 @@ const Preferences = ({ onClose }) => {
|
||||
}
|
||||
|
||||
case 'font': {
|
||||
return <Font />;
|
||||
return <Font close={onClose} />;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -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}
|
||||
|
@ -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 }) => {
|
||||
<CodeEditor
|
||||
collection={collection}
|
||||
theme={storedTheme}
|
||||
font={preferences.codeFont}
|
||||
font={get(preferences, 'font.codeFont', 'default')}
|
||||
value={bodyContent[bodyMode] || ''}
|
||||
onEdit={onEdit}
|
||||
onRun={onRun}
|
||||
|
@ -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 { updateRequestScript, updateResponseScript } 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 Script = ({ 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}
|
||||
|
@ -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}
|
||||
|
@ -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 <img src={item.requestSent.url} alt="image" />;
|
||||
}
|
||||
|
||||
return <CodeEditor collection={collection} font={preferences.codeFont} theme={storedTheme} onRun={onRun} value={value} mode={mode} readOnly />;
|
||||
return (
|
||||
<CodeEditor
|
||||
collection={collection}
|
||||
font={get(preferences, 'font.codeFont', 'default')}
|
||||
theme={storedTheme}
|
||||
onRun={onRun}
|
||||
value={value}
|
||||
mode={mode}
|
||||
readOnly
|
||||
/>
|
||||
);
|
||||
}, [tab, collection, storedTheme, onRun, value, mode]);
|
||||
|
||||
return (
|
||||
|
@ -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 <CodeEditor readOnly value={snippet} font={preferences.codeFont} theme={storedTheme} mode={lang} />;
|
||||
return (
|
||||
<CodeEditor
|
||||
readOnly
|
||||
value={snippet}
|
||||
font={get(preferences, 'font.codeFont', 'default')}
|
||||
theme={storedTheme}
|
||||
mode={lang}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default CodeView;
|
||||
|
@ -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 }) {
|
||||
<ThemeProvider>
|
||||
<ToastProvider>
|
||||
<AppProvider>
|
||||
<PreferencesProvider>
|
||||
<HotkeysProvider>
|
||||
<Component {...pageProps} />
|
||||
</HotkeysProvider>
|
||||
</PreferencesProvider>
|
||||
<HotkeysProvider>
|
||||
<Component {...pageProps} />
|
||||
</HotkeysProvider>
|
||||
</AppProvider>
|
||||
</ToastProvider>
|
||||
</ThemeProvider>
|
||||
|
@ -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();
|
||||
|
@ -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;
|
@ -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 (
|
||||
<PreferencesContext.Provider value={value}>
|
||||
<>{props.children}</>
|
||||
</PreferencesContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
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;
|
@ -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;
|
||||
|
@ -1057,7 +1057,6 @@ export const collectionsSlice = createSlice({
|
||||
if (collection) {
|
||||
collection.root = file.data;
|
||||
}
|
||||
console.log('collectionAddFileEvent', file);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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');
|
||||
|
35
packages/bruno-electron/src/ipc/preferences.js
Normal file
35
packages/bruno-electron/src/ipc/preferences.js
Normal file
@ -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;
|
@ -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
|
||||
};
|
||||
|
@ -1,4 +1,3 @@
|
||||
const _ = require('lodash');
|
||||
const Store = require('electron-store');
|
||||
|
||||
const DEFAULT_WINDOW_WIDTH = 1280;
|
||||
|
Loading…
Reference in New Issue
Block a user