summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorOskar Nyberg <oskar@mullvad.net>2024-04-09 08:09:38 +0200
committerOskar Nyberg <oskar@mullvad.net>2024-04-11 17:21:23 +0200
commit0470304a2b365c5062b26f9d817e9f9a0c23dad4 (patch)
tree6e0e2cf871ede93320b0bbc1097dd4ad87519101
parent41a01db5963e0f499c0dc23419af8ed6bd8bf772 (diff)
downloadmullvadvpn-0470304a2b365c5062b26f9d817e9f9a0c23dad4.tar.xz
mullvadvpn-0470304a2b365c5062b26f9d817e9f9a0c23dad4.zip
Add view for editing custom bridge
-rw-r--r--gui/src/renderer/components/AppRouter.tsx2
-rw-r--r--gui/src/renderer/components/EditCustomBridge.tsx116
-rw-r--r--gui/src/renderer/lib/routes.ts1
-rw-r--r--gui/src/shared/localization-contexts.ts1
4 files changed, 120 insertions, 0 deletions
diff --git a/gui/src/renderer/components/AppRouter.tsx b/gui/src/renderer/components/AppRouter.tsx
index 9e4a13adc8..0f85601a92 100644
--- a/gui/src/renderer/components/AppRouter.tsx
+++ b/gui/src/renderer/components/AppRouter.tsx
@@ -12,6 +12,7 @@ import Connect from './Connect';
import Debug from './Debug';
import { DeviceRevokedView } from './DeviceRevokedView';
import { EditApiAccessMethod } from './EditApiAccessMethod';
+import { EditCustomBridge } from './EditCustomBridge';
import {
SetupFinished,
TimeAdded,
@@ -92,6 +93,7 @@ export default function AppRouter() {
<Route exact path={RoutePath.problemReport} component={ProblemReport} />
<Route exact path={RoutePath.debug} component={Debug} />
<Route exact path={RoutePath.selectLocation} component={SelectLocation} />
+ <Route exact path={RoutePath.editCustomBridge} component={EditCustomBridge} />
<Route exact path={RoutePath.filter} component={Filter} />
</Switch>
</TransitionView>
diff --git a/gui/src/renderer/components/EditCustomBridge.tsx b/gui/src/renderer/components/EditCustomBridge.tsx
new file mode 100644
index 0000000000..2692217ea0
--- /dev/null
+++ b/gui/src/renderer/components/EditCustomBridge.tsx
@@ -0,0 +1,116 @@
+import { useCallback } from 'react';
+
+import { CustomProxy } from '../../shared/daemon-rpc-types';
+import { messages } from '../../shared/gettext';
+import { useBridgeSettingsUpdater } from '../lib/constraint-updater';
+import { useHistory } from '../lib/history';
+import { useBoolean } from '../lib/utilityHooks';
+import { useSelector } from '../redux/store';
+import { SettingsForm } from './cell/SettingsForm';
+import { BackAction } from './KeyboardNavigation';
+import { Layout, SettingsContainer } from './Layout';
+import { ModalAlert, ModalAlertType } from './Modal';
+import { NavigationBar, NavigationContainer, NavigationItems, TitleBarItem } from './NavigationBar';
+import { ProxyForm } from './ProxyForm';
+import SettingsHeader, { HeaderTitle } from './SettingsHeader';
+import { StyledContent, StyledNavigationScrollbars, StyledSettingsContent } from './SettingsStyles';
+import { SmallButton, SmallButtonColor } from './SmallButton';
+
+export function EditCustomBridge() {
+ return (
+ <SettingsForm>
+ <CustomBridgeForm />
+ </SettingsForm>
+ );
+}
+
+function CustomBridgeForm() {
+ const history = useHistory();
+ const bridgeSettingsUpdater = useBridgeSettingsUpdater();
+ const bridgeSettings = useSelector((state) => state.settings.bridgeSettings);
+
+ const [deleteDialogVisible, showDeleteDialog, hideDeleteDialog] = useBoolean();
+
+ // If there are no custom bridge settings, we should prompt the user to add a custom bridge.
+ // Otherwise, we should prompt them to edit the existing custom bridge settings.
+ const title =
+ bridgeSettings.custom === undefined
+ ? messages.pgettext('custom-bridge', 'Add custom bridge')
+ : messages.pgettext('custom-bridge', 'Edit custom bridge');
+
+ const onSave = useCallback(
+ (newBridge: CustomProxy) => {
+ void bridgeSettingsUpdater((bridgeSettings) => {
+ bridgeSettings.type = 'custom';
+ bridgeSettings.custom = newBridge;
+ return bridgeSettings;
+ });
+ history.pop();
+ },
+ [bridgeSettingsUpdater, history.pop],
+ );
+
+ const onDelete = useCallback(() => {
+ if (bridgeSettings.custom !== undefined) {
+ hideDeleteDialog();
+ void bridgeSettingsUpdater((bridgeSettings) => {
+ bridgeSettings.type = 'normal';
+ delete bridgeSettings.custom;
+ return bridgeSettings;
+ });
+ history.pop();
+ }
+ }, [bridgeSettingsUpdater, history.pop]);
+
+ return (
+ <BackAction action={history.pop}>
+ <Layout>
+ <SettingsContainer>
+ <NavigationContainer>
+ <NavigationBar>
+ <NavigationItems>
+ <TitleBarItem>{title}</TitleBarItem>
+ </NavigationItems>
+ </NavigationBar>
+
+ <StyledNavigationScrollbars fillContainer>
+ <StyledContent>
+ <SettingsHeader>
+ <HeaderTitle>{title}</HeaderTitle>
+ </SettingsHeader>
+
+ <StyledSettingsContent>
+ <ProxyForm
+ proxy={bridgeSettings.custom}
+ onSave={onSave}
+ onCancel={history.pop}
+ onDelete={bridgeSettings.custom === undefined ? undefined : showDeleteDialog}
+ />
+ </StyledSettingsContent>
+
+ <ModalAlert
+ isOpen={deleteDialogVisible}
+ type={ModalAlertType.warning}
+ gridButtons={[
+ <SmallButton key="cancel" onClick={hideDeleteDialog}>
+ {messages.gettext('Cancel')}
+ </SmallButton>,
+ <SmallButton key="delete" color={SmallButtonColor.red} onClick={onDelete}>
+ {messages.gettext('Delete')}
+ </SmallButton>,
+ ]}
+ close={hideDeleteDialog}
+ title={messages.pgettext('custom-bridge', 'Delete custom bridge?')}
+ message={messages.pgettext(
+ 'custom-bridge',
+ 'Deleting the custom bridge will take you back to the select location view and the Automatic option will be selected instead.',
+ )}
+ />
+ </StyledContent>
+ </StyledNavigationScrollbars>
+ </NavigationContainer>
+ </SettingsContainer>
+ </Layout>
+ </BackAction>
+ );
+}
diff --git a/gui/src/renderer/lib/routes.ts b/gui/src/renderer/lib/routes.ts
index 499157976a..1f2a3c28a7 100644
--- a/gui/src/renderer/lib/routes.ts
+++ b/gui/src/renderer/lib/routes.ts
@@ -25,5 +25,6 @@ export enum RoutePath {
problemReport = '/settings/support/problem-report',
debug = '/settings/debug',
selectLocation = '/select-location',
+ editCustomBridge = '/select-location/edit-custom-bridge',
filter = '/select-location/filter',
}
diff --git a/gui/src/shared/localization-contexts.ts b/gui/src/shared/localization-contexts.ts
index 71a663def0..f30212025c 100644
--- a/gui/src/shared/localization-contexts.ts
+++ b/gui/src/shared/localization-contexts.ts
@@ -15,6 +15,7 @@ export type LocalizationContexts =
| 'account-expiry'
| 'select-location-view'
| 'select-location-nav'
+ | 'custom-bridge'
| 'filter-view'
| 'filter-nav'
| 'settings-view'