diff --git a/app/actions/SettingsActions.js b/app/actions/SettingsActions.js index b39cbb99c4..44e2932802 100644 --- a/app/actions/SettingsActions.js +++ b/app/actions/SettingsActions.js @@ -93,8 +93,8 @@ export const saveSettings = (settings) => async (dispatch, getState) => { wallet.setupProxy(); } - if (needNetworkReset) { - dispatch(closeWalletRequest()); + if (needNetworkReset || updatedProxy) { + await dispatch(closeWalletRequest()); await dispatch(closeDaemonRequest()); dispatch(backToCredentials()); } @@ -156,7 +156,9 @@ export function updateStateSettingsChanged(settings, norestart) { const networkChange = { network: true, spvMode: true, - daemonStartAdvanced: true + daemonStartAdvanced: true, + proxyType: true, + proxyLocation: true }; const newDiffersFromTemp = settingsFields.reduce( diff --git a/app/components/views/SettingsPage/ConnectivitySettingsTab/ProxySettings/ProxySettings.jsx b/app/components/views/SettingsPage/ConnectivitySettingsTab/ProxySettings/ProxySettings.jsx index 6662aa19a8..1b3a117feb 100644 --- a/app/components/views/SettingsPage/ConnectivitySettingsTab/ProxySettings/ProxySettings.jsx +++ b/app/components/views/SettingsPage/ConnectivitySettingsTab/ProxySettings/ProxySettings.jsx @@ -8,6 +8,8 @@ import { } from "constants"; import styles from "./ProxySettings.module.css"; import { Label, Box } from "../../helpers"; +import { useProxySettings } from "./hooks"; +import { Button } from "pi-ui"; const availableProxyTypes = [ { name: , value: null }, @@ -17,41 +19,57 @@ const availableProxyTypes = [ { name: "SOCKS5", value: PROXYTYPE_SOCKS5 } ]; -const ProxySettings = ({ tempSettings, onChangeTempSettings }) => ( - -
- - - onChangeTempSettings({ proxyType: newProxyType.value }) - } - valueKey="value" - labelKey="name" - ariaLabelledBy="proxy-type-input" - options={availableProxyTypes} - /> -
- -
- - onChangeTempSettings({ proxyLocation: value })} - /> -
-
-); +const ProxySettings = ({ tempSettings, onChangeTempSettings }) => { + const { proxyType, proxyLocation, setProxyType, setProxyLocation } = + useProxySettings(tempSettings); + + const isProxySettingsChanged = + proxyType !== tempSettings.proxyType || + proxyLocation !== tempSettings.proxyLocation; + + return ( + +
+ + setProxyType(newProxyType.value)} + valueKey="value" + labelKey="name" + ariaLabelledBy="proxy-type-input" + options={availableProxyTypes} + /> +
+ +
+ + setProxyLocation(value)} + /> +
+ + {isProxySettingsChanged && ( + + )} +
+ ); +}; ProxySettings.propTypes = { tempSettings: PropTypes.object.isRequired, diff --git a/app/components/views/SettingsPage/ConnectivitySettingsTab/ProxySettings/ProxySettings.module.css b/app/components/views/SettingsPage/ConnectivitySettingsTab/ProxySettings/ProxySettings.module.css index 3151a9ce6a..80cc95faff 100644 --- a/app/components/views/SettingsPage/ConnectivitySettingsTab/ProxySettings/ProxySettings.module.css +++ b/app/components/views/SettingsPage/ConnectivitySettingsTab/ProxySettings/ProxySettings.module.css @@ -1,6 +1,6 @@ .box { display: grid; - grid-gap: 3rem; + grid-gap: 1rem 3rem; grid-template-columns: repeat(2, 1fr); } @@ -8,8 +8,16 @@ color: var(--main-dark-blue) !important; } +.submitButton { + grid-column: 2; +} + @media (--sm-viewport) { .box { grid-template-columns: 1fr; } + + .submitButton { + grid-column: 1; + } } diff --git a/app/components/views/SettingsPage/ConnectivitySettingsTab/ProxySettings/hooks.js b/app/components/views/SettingsPage/ConnectivitySettingsTab/ProxySettings/hooks.js new file mode 100644 index 0000000000..a12a3b746b --- /dev/null +++ b/app/components/views/SettingsPage/ConnectivitySettingsTab/ProxySettings/hooks.js @@ -0,0 +1,15 @@ +import { useState } from "react"; + +export const useProxySettings = (tempSettings) => { + const [proxyType, setProxyType] = useState(tempSettings.proxyType); + const [proxyLocation, setProxyLocation] = useState( + tempSettings.proxyLocation + ); + + return { + proxyType, + proxyLocation, + setProxyType, + setProxyLocation + }; +}; diff --git a/app/main_dev/launch.js b/app/main_dev/launch.js index bcd5bc5f44..e72abfacb9 100644 --- a/app/main_dev/launch.js +++ b/app/main_dev/launch.js @@ -28,7 +28,8 @@ import { UPGD_ELECTRON8, CSPP_URL, CSPP_PORT_TESTNET, - CSPP_PORT_MAINNET + CSPP_PORT_MAINNET, + PROXYTYPE_SOCKS5 } from "constants"; import * as cfgConstants from "constants/config"; import os from "os"; @@ -43,6 +44,7 @@ import ini from "ini"; import { makeRandomString, isPlainString as isString } from "helpers/strings"; import { makeFileBackup } from "helpers/files"; import { DEX_LOCALPAGE } from "./externalRequests"; +import { getProxyTypeAndLocation } from "./proxy"; const argv = parseArgs(process.argv.slice(1), OPTIONS); const debug = argv.debug || process.env.NODE_ENV === "development"; @@ -390,6 +392,15 @@ export const launchDCRD = (reactIPC, testnet, appdata) => args.push("--testnet"); } + const { proxyType, proxyLocation } = getProxyTypeAndLocation(); + logger.log( + "info", + `ProxyType: ${proxyType}, ProxyLocation: ${proxyLocation}` + ); + if (proxyType === PROXYTYPE_SOCKS5 && proxyLocation) { + args.push(`--proxy=${proxyLocation}`); + } + rpcuser = rpc_user; rpcpass = rpc_pass; rpccert = rpc_cert; @@ -644,6 +655,15 @@ export const launchDCRWallet = async ( : "--csppserver=" + CSPP_URL + ":" + CSPP_PORT_TESTNET ); + const { proxyType, proxyLocation } = getProxyTypeAndLocation(); + logger.log( + "info", + `ProxyType: ${proxyType}, ProxyLocation: ${proxyLocation}` + ); + if (proxyType === PROXYTYPE_SOCKS5 && proxyLocation) { + args.push(`--proxy=${proxyLocation}`); + } + const dcrwExe = getExecutablePath("dcrwallet", argv.custombinpath); if (!fs.existsSync(dcrwExe)) { const msg = `The dcrwallet executable does not exist. Expected to find it at ${dcrwExe}`; diff --git a/app/main_dev/proxy.js b/app/main_dev/proxy.js index d0276fe42e..5683e969e3 100644 --- a/app/main_dev/proxy.js +++ b/app/main_dev/proxy.js @@ -11,10 +11,7 @@ import { export const setupProxy = (logger) => new Promise((resolve, reject) => { - const cfg = getGlobalCfg(); - - const proxyType = cfg.get(cfgConstants.PROXY_TYPE); - const proxyLocation = cfg.get(cfgConstants.PROXY_LOCATION); + const { proxyType, proxyLocation } = getProxyTypeAndLocation(); const proxyConfig = { pacScript: null, @@ -58,3 +55,12 @@ export const setupProxy = (logger) => }) .catch(reject); }); + +export const getProxyTypeAndLocation = () => { + const cfg = getGlobalCfg(); + + const proxyType = cfg.get(cfgConstants.PROXY_TYPE); + const proxyLocation = cfg.get(cfgConstants.PROXY_LOCATION); + + return { proxyType, proxyLocation }; +}; diff --git a/test/unit/actions/SettingsActions.spec.js b/test/unit/actions/SettingsActions.spec.js index 8c6781aac2..d2047a2883 100644 --- a/test/unit/actions/SettingsActions.spec.js +++ b/test/unit/actions/SettingsActions.spec.js @@ -53,8 +53,6 @@ const testSettings = { EXTERNALREQUEST_UPDATE_CHECK, EXTERNALREQUEST_DEX ], - proxyType: "new-proxy-type", - proxyLocation: "new-proxy-location", spvMode: false, spvModeFromCli: false, spvConnect: [], @@ -239,6 +237,74 @@ test("test saveSettings", async () => { // locale has not been changed // dcrdata is enabled now test("test saveSettings - save alternative data", async () => { + const testSettingsCopy = { + ...testSettings, + allowedExternalRequests: [ + ...testSettings.allowedExternalRequests, + EXTERNALREQUEST_POLITEIA, + EXTERNALREQUEST_DCRDATA + ] + }; + mockGlobalCfgGet = jest.fn((key) => { + switch (key) { + case ALLOWED_EXTERNAL_REQUESTS: + return testSettingsCopy.allowedExternalRequests; + } + }); + const store = createStore( + cloneDeep({ + ...initialState, + daemon: {}, + settings: { + ...initialState.settings, + needNetworkReset: false, + currentSettings: { + ...initialState.settings.currentSettings, + network: TESTNET, + locale: testSettingsCopy.locale + } + } + }) + ); + await store.dispatch(settingsActions.saveSettings(testSettingsCopy)); + + expect(mockGetGlobalCfg).toHaveBeenCalled(); + // locale has not been changed + expect(mockChangeMenuLocale).not.toHaveBeenCalled(); + + // politeia is enabled now + expect(mockGetTokenAndInitialBatch).toHaveBeenCalled(); + expect(mockResetInventoryAndProposals).not.toHaveBeenCalled(); + + // dcrdata is enabled now + expect(mockGetTreasuryBalance).toHaveBeenCalled(); + expect(mockResetTreasuryBalance).not.toHaveBeenCalled(); + + // proxy has been changed, even though just the proxy location has been changed + expect(mockSetupProxy).not.toHaveBeenCalled(); + + // don't a need network reset + expect(mockCloseWalletRequest).not.toHaveBeenCalled(); + expect(mockCloseDaemonRequest).not.toHaveBeenCalled(); + expect(mockBackToCredentials).not.toHaveBeenCalled(); + + // allowed external requests has not been changed + expect(mockReloadAllowedExternalRequests).not.toHaveBeenCalled(); + + // wallet is not openned + expect(mockGetWalletCfg).not.toHaveBeenCalled(); + expect(mockWalletCfgSet).not.toHaveBeenCalled(); + + expect( + isEqual(store.getState().settings.currentSettings, testSettingsCopy) + ).toBeTruthy(); + expect( + isEqual(store.getState().settings.tempSettings, testSettingsCopy) + ).toBeTruthy(); + expect(store.getState().settings.settingsChanged).toBeFalsy(); +}); + +test("test saveSettings - proxy change needs wallet restart", async () => { const testSettingsCopy = { ...testSettings, allowedExternalRequests: [ @@ -267,7 +333,9 @@ test("test saveSettings - save alternative data", async () => { currentSettings: { ...initialState.settings.currentSettings, network: TESTNET, - locale: testSettingsCopy.locale + locale: testSettingsCopy.locale, + proxyType: "new-proxy-type", + proxyLocation: "new-proxy-location" } } }) @@ -290,9 +358,9 @@ test("test saveSettings - save alternative data", async () => { expect(mockSetupProxy).toHaveBeenCalled(); // don't a need network reset - expect(mockCloseWalletRequest).not.toHaveBeenCalled(); - expect(mockCloseDaemonRequest).not.toHaveBeenCalled(); - expect(mockBackToCredentials).not.toHaveBeenCalled(); + expect(mockCloseWalletRequest).toHaveBeenCalled(); + expect(mockCloseDaemonRequest).toHaveBeenCalled(); + expect(mockBackToCredentials).toHaveBeenCalled(); // allowed external requests has not been changed expect(mockReloadAllowedExternalRequests).not.toHaveBeenCalled(); diff --git a/test/unit/components/views/SettingsPage/SettingsPage.spec.js b/test/unit/components/views/SettingsPage/SettingsPage.spec.js index 7e53d4368f..dac090e6cd 100644 --- a/test/unit/components/views/SettingsPage/SettingsPage.spec.js +++ b/test/unit/components/views/SettingsPage/SettingsPage.spec.js @@ -393,7 +393,6 @@ test.each([ { daemonStartAdvanced: true }, true ], - ["Proxy Type", "HTTP", "PAC", { proxyType: PROXYTYPE_PAC }, false], [ "Locale", testDefaultLocaleLabel, @@ -468,13 +467,6 @@ test.each([ testSpvConnectValue.join(","), { spvConnect: testSpvConnectValue }, false - ], - [ - "Proxy Location", - testDefaultProxyLocation, - testProxyLocation, - { proxyLocation: testProxyLocation }, - false ] ])("change '%s' TextInput from '%s' to '%s' expeced %s", testTextFieldInput); @@ -845,3 +837,43 @@ test("renders settings with trezor is NOT enabled", () => { expect(mockIsTrezor).toHaveBeenCalled(); mockIsTrezor.mockRestore(); }); + +test("test proxy settings", async () => { + render(, { initialState: { settings: testSettings } }); + + // set proxy type + const inputControl = screen.getByLabelText("Proxy Type"); + const oldValue = "HTTP"; + const option = "PAC"; + const inputValueSpan = getOptionByNameAndType(oldValue, "singleValue"); + expect( + screen.queryByRole("button", { + name: "Save proxy settings" + }) + ).not.toBeInTheDocument(); + expect(inputValueSpan.textContent).toMatch(oldValue); + const changeFn = () => { + user.click(inputControl); + user.click(getOptionByNameAndType(option, "option")); + }; + changeFn(); + + // set proxy location + const proxyLocationInputControl = screen.getByLabelText("Proxy Location"); + expect(proxyLocationInputControl.value).toMatch(testDefaultProxyLocation); + user.clear(proxyLocationInputControl); + user.type(proxyLocationInputControl, testProxyLocation); + // press enter + fireEvent.keyDown(proxyLocationInputControl, { key: "enter", keyCode: 13 }); + + user.click(screen.getByRole("button", { name: "Save proxy settings" })); + await wait(() => screen.getByText("Reset required")); + user.click(screen.getByRole("button", { name: "Confirm" })); + + await wait(() => + expect(mockSaveSettings).toHaveBeenCalledWith({ + ...testCurrentSettings, + ...{ proxyType: PROXYTYPE_PAC, proxyLocation: testProxyLocation } + }) + ); +});