summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorOskar Nyberg <oskar@mullvad.net>2022-03-30 09:23:28 +0200
committerOskar Nyberg <oskar@mullvad.net>2022-03-31 15:59:47 +0200
commita7d0e178604e740c695003ab2c99ef0216a516d2 (patch)
tree48ad63e293336314bc7777e1ad15c7ad52592079
parent296277eb75a58eff48addf31a7e8f4cf4d7ab4fd (diff)
downloadmullvadvpn-a7d0e178604e740c695003ab2c99ef0216a516d2.tar.xz
mullvadvpn-a7d0e178604e740c695003ab2c99ef0216a516d2.zip
Add button to delete browsed for split tunneling apps
-rw-r--r--gui/src/main/gui-settings.ts9
-rw-r--r--gui/src/main/index.ts70
-rw-r--r--gui/src/main/windows-split-tunneling.ts26
-rw-r--r--gui/src/renderer/app.tsx14
-rw-r--r--gui/src/renderer/components/SplitTunnelingSettings.tsx111
-rw-r--r--gui/src/renderer/components/SplitTunnelingSettingsStyles.tsx2
-rw-r--r--gui/src/renderer/redux/settings/actions.ts6
-rw-r--r--gui/src/renderer/redux/settings/reducers.ts4
-rw-r--r--gui/src/shared/application-types.ts4
-rw-r--r--gui/src/shared/ipc-schema.ts13
10 files changed, 154 insertions, 105 deletions
diff --git a/gui/src/main/gui-settings.ts b/gui/src/main/gui-settings.ts
index 73c546a24b..a4e7a31d22 100644
--- a/gui/src/main/gui-settings.ts
+++ b/gui/src/main/gui-settings.ts
@@ -90,6 +90,15 @@ export default class GuiSettings {
});
}
+ public deleteBrowsedForSplitTunnelingApplications(path: string) {
+ this.changeStateAndNotify({
+ ...this.stateValue,
+ browsedForSplitTunnelingApplications: this.browsedForSplitTunnelingApplications.filter(
+ (application) => application !== path,
+ ),
+ });
+ }
+
get browsedForSplitTunnelingApplications(): Array<string> {
return this.stateValue.browsedForSplitTunnelingApplications;
}
diff --git a/gui/src/main/index.ts b/gui/src/main/index.ts
index a6fcfa7be7..4ab0cbdd93 100644
--- a/gui/src/main/index.ts
+++ b/gui/src/main/index.ts
@@ -18,7 +18,7 @@ import * as path from 'path';
import util from 'util';
import config from '../config.json';
import { closeToExpiry, hasExpired } from '../shared/account-expiry';
-import { IApplication } from '../shared/application-types';
+import { IWindowsApplication } from '../shared/application-types';
import BridgeSettingsBuilder from '../shared/bridge-settings-builder';
import {
AccountToken,
@@ -241,7 +241,7 @@ class ApplicationMain {
private rendererLog?: Logger;
private translations: ITranslations = { locale: this.locale };
- private windowsSplitTunnelingApplications?: IApplication[];
+ private windowsSplitTunnelingApplications?: IWindowsApplication[];
private macOsScrollbarVisibility?: MacOsScrollbarVisibility;
@@ -1331,66 +1331,40 @@ class ApplicationMain {
});
IpcMainEventChannel.linuxSplitTunneling.handleGetApplications(() => {
- if (linuxSplitTunneling) {
- return linuxSplitTunneling.getApplications(this.locale);
- } else {
- throw Error('linuxSplitTunneling.getApplications function called without being imported');
- }
+ return linuxSplitTunneling.getApplications(this.locale);
});
IpcMainEventChannel.windowsSplitTunneling.handleGetApplications((updateCaches: boolean) => {
- if (windowsSplitTunneling) {
- return windowsSplitTunneling.getApplications({
- updateCaches,
- });
- } else {
- throw Error('windowsSplitTunneling.getApplications function called without being imported');
- }
+ return windowsSplitTunneling.getApplications({ updateCaches });
});
IpcMainEventChannel.linuxSplitTunneling.handleLaunchApplication((application) => {
- if (linuxSplitTunneling) {
- return linuxSplitTunneling.launchApplication(application);
- } else {
- throw Error('linuxSplitTunneling.launchApplication function called without being imported');
- }
+ return linuxSplitTunneling.launchApplication(application);
});
IpcMainEventChannel.windowsSplitTunneling.handleSetState((enabled) => {
- if (windowsSplitTunneling) {
- return this.daemonRpc.setSplitTunnelingState(enabled);
- } else {
- throw Error('windowsSplitTunneling.setState function called without being imported');
- }
+ return this.daemonRpc.setSplitTunnelingState(enabled);
});
IpcMainEventChannel.windowsSplitTunneling.handleAddApplication(async (application) => {
- if (windowsSplitTunneling) {
- // If the applications is a string (path) it's an application picked with the file picker
- // that we want to add to the list of additional applications.
- if (typeof application === 'string') {
- this.guiSettings.addBrowsedForSplitTunnelingApplications(application);
- const applicationPath = await windowsSplitTunneling.addApplicationPathToCache(
- application,
- );
- await this.daemonRpc.addSplitTunnelingApplication(applicationPath);
- } else {
- await this.daemonRpc.addSplitTunnelingApplication(application.absolutepath);
- }
+ // If the applications is a string (path) it's an application picked with the file picker
+ // that we want to add to the list of additional applications.
+ if (typeof application === 'string') {
+ this.guiSettings.addBrowsedForSplitTunnelingApplications(application);
+ const applicationPath = await windowsSplitTunneling.addApplicationPathToCache(application);
+ await this.daemonRpc.addSplitTunnelingApplication(applicationPath);
} else {
- throw Error(
- 'windowsSplitTunneling.handleAddApplication function called without being imported',
- );
+ await this.daemonRpc.addSplitTunnelingApplication(application.absolutepath);
}
});
IpcMainEventChannel.windowsSplitTunneling.handleRemoveApplication((application) => {
- if (windowsSplitTunneling) {
- return this.daemonRpc.removeSplitTunnelingApplication(
- typeof application === 'string' ? application : application.absolutepath,
- );
- } else {
- throw Error(
- 'windowsSplitTunneling.handleRemoveApplication function called without being imported',
- );
- }
+ return this.daemonRpc.removeSplitTunnelingApplication(
+ typeof application === 'string' ? application : application.absolutepath,
+ );
});
+ IpcMainEventChannel.windowsSplitTunneling.handleForgetManuallyAddedApplication(
+ (application) => {
+ this.guiSettings.deleteBrowsedForSplitTunnelingApplications(application.absolutepath);
+ return windowsSplitTunneling.removeApplicationFromCache(application);
+ },
+ );
IpcMainEventChannel.problemReport.handleCollectLogs((toRedact) => {
const id = randomUUID();
diff --git a/gui/src/main/windows-split-tunneling.ts b/gui/src/main/windows-split-tunneling.ts
index a8915595e5..2ae118db4a 100644
--- a/gui/src/main/windows-split-tunneling.ts
+++ b/gui/src/main/windows-split-tunneling.ts
@@ -1,7 +1,7 @@
import { app, shell } from 'electron';
import fs from 'fs';
import path from 'path';
-import { IApplication } from '../shared/application-types';
+import { IWindowsApplication } from '../shared/application-types';
import log from '../shared/logging';
import {
ArrayValue,
@@ -35,6 +35,7 @@ interface ShortcutDetails {
target: string;
name: string;
args?: string;
+ deletable: boolean;
}
type RvaToOffset = (rva: number) => Promise<number>;
@@ -58,9 +59,9 @@ const APPLICATION_ALLOW_LIST = [
// Cache of all previously scanned shortcuts.
const shortcutCache: Record<string, ShortcutDetails> = {};
// Cache of all previously scanned applications.
-const applicationCache: Record<string, IApplication> = {};
+const applicationCache: Record<string, IWindowsApplication> = {};
// List of shortcuts that have been added manually by the user.
-const additionalShortcuts: ShortcutDetails[] = [];
+let additionalShortcuts: ShortcutDetails[] = [];
// Finds applications by searching through the startmenu for shortcuts with and exe-file as target.
// If applicationPaths has a value, the returned applications are only the ones corresponding to
@@ -68,7 +69,7 @@ const additionalShortcuts: ShortcutDetails[] = [];
export async function getApplications(options: {
applicationPaths?: string[];
updateCaches?: boolean;
-}): Promise<{ fromCache: boolean; applications: IApplication[] }> {
+}): Promise<{ fromCache: boolean; applications: IWindowsApplication[] }> {
const cacheIsEmpty = Object.keys(shortcutCache).length === 0;
if (options.updateCaches || cacheIsEmpty) {
@@ -105,7 +106,11 @@ export async function addApplicationPathToCache(applicationPath: string): Promis
const parsedPath = path.parse(applicationPath);
if (parsedPath.ext === '.lnk') {
const shortcutDetiails = shell.readShortcutLink(path.resolve(applicationPath));
- additionalShortcuts.push({ ...shortcutDetiails, name: path.parse(applicationPath).name });
+ additionalShortcuts.push({
+ ...shortcutDetiails,
+ name: path.parse(applicationPath).name,
+ deletable: true,
+ });
return shortcutDetiails.target;
} else {
await addApplicationToAdditionalShortcuts(applicationPath);
@@ -113,6 +118,13 @@ export async function addApplicationPathToCache(applicationPath: string): Promis
}
}
+export function removeApplicationFromCache(application: IWindowsApplication): void {
+ additionalShortcuts = additionalShortcuts.filter(
+ (shortcut) => shortcut.target !== application.absolutepath,
+ );
+ delete applicationCache[application.absolutepath.toLowerCase()];
+}
+
// Reads the start-menu directories and adds all shortcuts, targeting applications using networking,
// to the shortcuts cache. Wheter or not an application use networking is determined by checking for
// "WS2_32.dll" in it's imports.
@@ -158,6 +170,7 @@ async function addApplicationToAdditionalShortcuts(applicationPath: string): Pro
additionalShortcuts.push({
target: applicationPath,
name: (await getProgramName(applicationPath)) ?? path.parse(applicationPath).name,
+ deletable: true,
});
}
}
@@ -231,11 +244,12 @@ function removeDuplicates(shortcuts: ShortcutDetails[]): ShortcutDetails[] {
async function convertToSplitTunnelingApplication(
shortcut: ShortcutDetails,
-): Promise<IApplication> {
+): Promise<IWindowsApplication> {
return {
absolutepath: shortcut.target,
name: shortcut.name,
icon: await retrieveIcon(shortcut.target),
+ deletable: shortcut.deletable,
};
}
diff --git a/gui/src/renderer/app.tsx b/gui/src/renderer/app.tsx
index 778bf4cdbd..4d86e183cc 100644
--- a/gui/src/renderer/app.tsx
+++ b/gui/src/renderer/app.tsx
@@ -16,7 +16,7 @@ import userInterfaceActions from './redux/userinterface/actions';
import versionActions from './redux/version/actions';
import { IChangelog, ICurrentAppVersionInfo } from '../shared/ipc-types';
-import { IApplication, ILinuxSplitTunnelingApplication } from '../shared/application-types';
+import { IWindowsApplication, ILinuxSplitTunnelingApplication } from '../shared/application-types';
import { IGuiSettingsState, SYSTEM_PREFERRED_LOCALE_KEY } from '../shared/gui-settings-state';
import { messages, relayLocations } from '../shared/gettext';
import log, { ConsoleOutput } from '../shared/logging';
@@ -171,7 +171,7 @@ export default class AppRenderer {
this.storeAutoStart(autoStart);
});
- IpcRendererEventChannel.windowsSplitTunneling.listen((applications: IApplication[]) => {
+ IpcRendererEventChannel.windowsSplitTunneling.listen((applications: IWindowsApplication[]) => {
this.reduxActions.settings.setSplitTunnelingApplications(applications);
});
@@ -471,14 +471,20 @@ export default class AppRenderer {
return IpcRendererEventChannel.windowsSplitTunneling.setState(enabled);
};
- public addSplitTunnelingApplication(application: IApplication | string): Promise<void> {
+ public addSplitTunnelingApplication(application: IWindowsApplication | string): Promise<void> {
return IpcRendererEventChannel.windowsSplitTunneling.addApplication(application);
}
- public removeSplitTunnelingApplication(application: IApplication | string) {
+ public removeSplitTunnelingApplication(application: IWindowsApplication) {
void IpcRendererEventChannel.windowsSplitTunneling.removeApplication(application);
}
+ public forgetManuallyAddedSplitTunnelingApplication(application: IWindowsApplication) {
+ return IpcRendererEventChannel.windowsSplitTunneling.forgetManuallyAddedApplication(
+ application,
+ );
+ }
+
public collectProblemReport(toRedact?: string): Promise<string> {
return IpcRendererEventChannel.problemReport.collectLogs(toRedact);
}
diff --git a/gui/src/renderer/components/SplitTunnelingSettings.tsx b/gui/src/renderer/components/SplitTunnelingSettings.tsx
index b6f2e3bd89..3cabb8d3c6 100644
--- a/gui/src/renderer/components/SplitTunnelingSettings.tsx
+++ b/gui/src/renderer/components/SplitTunnelingSettings.tsx
@@ -3,7 +3,11 @@ import { useSelector } from 'react-redux';
import { sprintf } from 'sprintf-js';
import { colors } from '../../config.json';
import { messages } from '../../shared/gettext';
-import { IApplication, ILinuxSplitTunnelingApplication } from '../../shared/application-types';
+import {
+ IApplication,
+ ILinuxSplitTunnelingApplication,
+ IWindowsApplication,
+} from '../../shared/application-types';
import { useAppContext } from '../context';
import { useHistory } from '../lib/history';
import { useAsyncEffect } from '../lib/utilityHooks';
@@ -153,6 +157,13 @@ function LinuxSplitTunnelingSettings(props: IPlatformSplitTunnelingSettingsProps
const hideBrowseFailureDialog = useCallback(() => setBrowseError(undefined), []);
+ const rowRenderer = useCallback(
+ (application: ILinuxSplitTunnelingApplication) => (
+ <LinuxApplicationRow application={application} onSelect={launchApplication} />
+ ),
+ [launchApplication],
+ );
+
return (
<>
<SettingsHeader>
@@ -166,11 +177,7 @@ function LinuxSplitTunnelingSettings(props: IPlatformSplitTunnelingSettingsProps
</SettingsHeader>
<SearchBar searchTerm={searchTerm} onSearch={setSearchTerm} />
- <ApplicationList
- applications={filteredApplications}
- onSelect={launchApplication}
- rowComponent={LinuxApplicationRow}
- />
+ <ApplicationList applications={filteredApplications} rowRenderer={rowRenderer} />
<StyledBrowseButton onClick={launchWithFilePicker}>
{messages.pgettext('split-tunneling-view', 'Find another app')}
@@ -287,6 +294,7 @@ export function WindowsSplitTunnelingSettings(props: IPlatformSplitTunnelingSett
const {
addSplitTunnelingApplication,
removeSplitTunnelingApplication,
+ forgetManuallyAddedSplitTunnelingApplication,
getWindowsSplitTunnelingApplications,
setSplitTunnelingState,
} = useAppContext();
@@ -296,7 +304,7 @@ export function WindowsSplitTunnelingSettings(props: IPlatformSplitTunnelingSett
);
const [searchTerm, setSearchTerm] = useState('');
- const [applications, setApplications] = useState<IApplication[]>();
+ const [applications, setApplications] = useState<IWindowsApplication[]>();
useAsyncEffect(async () => {
const { fromCache, applications } = await getWindowsSplitTunnelingApplications();
setApplications(applications);
@@ -327,7 +335,7 @@ export function WindowsSplitTunnelingSettings(props: IPlatformSplitTunnelingSett
}, [applications, splitTunnelingApplications, searchTerm]);
const addApplication = useCallback(
- async (application: IApplication | string) => {
+ async (application: IWindowsApplication | string) => {
if (!splitTunnelingEnabled) {
await setSplitTunnelingState(true);
}
@@ -337,7 +345,7 @@ export function WindowsSplitTunnelingSettings(props: IPlatformSplitTunnelingSett
);
const addApplicationAndUpdate = useCallback(
- async (application: IApplication | string) => {
+ async (application: IWindowsApplication | string) => {
await addApplication(application);
const { applications } = await getWindowsSplitTunnelingApplications();
setApplications(applications);
@@ -345,8 +353,17 @@ export function WindowsSplitTunnelingSettings(props: IPlatformSplitTunnelingSett
[addApplication, getWindowsSplitTunnelingApplications],
);
+ const forgetManuallyAddedApplicationAndUpdate = useCallback(
+ async (application: IWindowsApplication) => {
+ await forgetManuallyAddedSplitTunnelingApplication(application);
+ const { applications } = await getWindowsSplitTunnelingApplications();
+ setApplications(applications);
+ },
+ [forgetManuallyAddedSplitTunnelingApplication, getWindowsSplitTunnelingApplications],
+ );
+
const removeApplication = useCallback(
- async (application: IApplication) => {
+ async (application: IWindowsApplication) => {
if (!splitTunnelingEnabled) {
await setSplitTunnelingState(true);
}
@@ -367,6 +384,27 @@ export function WindowsSplitTunnelingSettings(props: IPlatformSplitTunnelingSett
await filePickerCallback();
}, [filePickerCallback, props.scrollToTop]);
+ const excludedRowRenderer = useCallback(
+ (application: IWindowsApplication) => (
+ <WindowsApplicationRow application={application} onRemove={removeApplication} />
+ ),
+ [removeApplication],
+ );
+
+ const includedRowRenderer = useCallback(
+ (application: IWindowsApplication) => {
+ const onForget = application.deletable ? forgetManuallyAddedApplicationAndUpdate : undefined;
+ return (
+ <WindowsApplicationRow
+ application={application}
+ onAdd={addApplication}
+ onDelete={onForget}
+ />
+ );
+ },
+ [addApplication, forgetManuallyAddedApplicationAndUpdate],
+ );
+
const showSplitSection = splitTunnelingEnabled && filteredSplitApplications.length > 0;
const showNonSplitSection =
splitTunnelingEnabled &&
@@ -396,8 +434,7 @@ export function WindowsSplitTunnelingSettings(props: IPlatformSplitTunnelingSett
</Cell.SectionTitle>
<ApplicationList
applications={filteredSplitApplications}
- onRemove={removeApplication}
- rowComponent={ApplicationRow}
+ rowRenderer={excludedRowRenderer}
/>
</Cell.Section>
</Accordion>
@@ -409,8 +446,7 @@ export function WindowsSplitTunnelingSettings(props: IPlatformSplitTunnelingSett
</Cell.SectionTitle>
<ApplicationList
applications={filteredNonSplitApplications}
- onSelect={addApplication}
- rowComponent={ApplicationRow}
+ rowRenderer={includedRowRenderer}
/>
</Cell.Section>
</Accordion>
@@ -442,9 +478,7 @@ export function WindowsSplitTunnelingSettings(props: IPlatformSplitTunnelingSett
interface IApplicationListProps<T extends IApplication> {
applications: T[] | undefined;
- onSelect?: (application: T) => void;
- onRemove?: (application: T) => void;
- rowComponent: React.ComponentType<IApplicationRowProps<T>>;
+ rowRenderer: (application: T) => React.ReactElement;
}
function ApplicationList<T extends IApplication>(props: IApplicationListProps<T>) {
@@ -458,14 +492,7 @@ function ApplicationList<T extends IApplication>(props: IApplicationListProps<T>
return (
<StyledListContainer>
<List items={props.applications} getKey={applicationGetKey}>
- {(application) => (
- <props.rowComponent
- key={application.absolutepath}
- application={application}
- onSelect={props.onSelect}
- onRemove={props.onRemove}
- />
- )}
+ {props.rowRenderer}
</List>
</StyledListContainer>
);
@@ -476,21 +503,26 @@ function applicationGetKey<T extends IApplication>(application: T): string {
return application.absolutepath;
}
-interface IApplicationRowProps<T extends IApplication> {
- application: T;
- onSelect?: (application: T) => void;
- onRemove?: (application: T) => void;
+interface IWindowsApplicationRowProps {
+ application: IWindowsApplication;
+ onAdd?: (application: IWindowsApplication) => void;
+ onRemove?: (application: IWindowsApplication) => void;
+ onDelete?: (application: IWindowsApplication) => void;
}
-function ApplicationRow<T extends IApplication>(props: IApplicationRowProps<T>) {
- const onSelect = useCallback(() => {
- props.onSelect?.(props.application);
- }, [props.onSelect, props.application]);
+function WindowsApplicationRow(props: IWindowsApplicationRowProps) {
+ const onAdd = useCallback(() => {
+ props.onAdd?.(props.application);
+ }, [props.onAdd, props.application]);
const onRemove = useCallback(() => {
props.onRemove?.(props.application);
}, [props.onRemove, props.application]);
+ const onDelete = useCallback(() => {
+ props.onDelete?.(props.application);
+ }, [props.onDelete, props.application]);
+
return (
<Cell.CellButton>
{props.application.icon ? (
@@ -499,11 +531,20 @@ function ApplicationRow<T extends IApplication>(props: IApplicationRowProps<T>)
<StyledIconPlaceholder />
)}
<StyledCellLabel>{props.application.name}</StyledCellLabel>
- {props.onSelect && (
+ {props.onDelete && (
+ <StyledActionIcon
+ source="icon-close"
+ width={18}
+ onClick={onDelete}
+ tintColor={colors.white40}
+ tintHoverColor={colors.white60}
+ />
+ )}
+ {props.onAdd && (
<StyledActionIcon
source="icon-add"
width={18}
- onClick={onSelect}
+ onClick={onAdd}
tintColor={colors.white40}
tintHoverColor={colors.white60}
/>
diff --git a/gui/src/renderer/components/SplitTunnelingSettingsStyles.tsx b/gui/src/renderer/components/SplitTunnelingSettingsStyles.tsx
index 054415db2a..b33c814545 100644
--- a/gui/src/renderer/components/SplitTunnelingSettingsStyles.tsx
+++ b/gui/src/renderer/components/SplitTunnelingSettingsStyles.tsx
@@ -49,7 +49,7 @@ export const StyledIcon = styled(Cell.UntintedIcon)(disabledApplication, {
});
export const StyledActionIcon = styled(ImageView)({
- marginLeft: '6px',
+ marginLeft: '8px',
});
export const StyledCellWarningIcon = styled(Cell.Icon)({
diff --git a/gui/src/renderer/redux/settings/actions.ts b/gui/src/renderer/redux/settings/actions.ts
index 32cd157157..1e12e7ec14 100644
--- a/gui/src/renderer/redux/settings/actions.ts
+++ b/gui/src/renderer/redux/settings/actions.ts
@@ -1,6 +1,6 @@
import { BridgeState, IDnsOptions } from '../../../shared/daemon-rpc-types';
import { IGuiSettingsState } from '../../../shared/gui-settings-state';
-import { IApplication } from '../../../shared/application-types';
+import { IWindowsApplication } from '../../../shared/application-types';
import { BridgeSettingsRedux, IRelayLocationRedux, RelaySettingsRedux } from './reducers';
export interface IUpdateGuiSettingsAction {
@@ -80,7 +80,7 @@ export interface IUpdateSplitTunnelingStateAction {
export interface ISetSplitTunnelingApplicationsAction {
type: 'SET_SPLIT_TUNNELING_APPLICATIONS';
- applications: IApplication[];
+ applications: IWindowsApplication[];
}
export type SettingsAction =
@@ -211,7 +211,7 @@ function updateSplitTunnelingState(enabled: boolean): IUpdateSplitTunnelingState
}
function setSplitTunnelingApplications(
- applications: IApplication[],
+ applications: IWindowsApplication[],
): ISetSplitTunnelingApplicationsAction {
return {
type: 'SET_SPLIT_TUNNELING_APPLICATIONS',
diff --git a/gui/src/renderer/redux/settings/reducers.ts b/gui/src/renderer/redux/settings/reducers.ts
index 9b55160a5e..a5dd938126 100644
--- a/gui/src/renderer/redux/settings/reducers.ts
+++ b/gui/src/renderer/redux/settings/reducers.ts
@@ -1,4 +1,4 @@
-import { IApplication } from '../../../shared/application-types';
+import { IWindowsApplication } from '../../../shared/application-types';
import {
BridgeState,
LiftedConstraint,
@@ -91,7 +91,7 @@ export interface ISettingsReduxState {
};
dns: IDnsOptions;
splitTunneling: boolean;
- splitTunnelingApplications: IApplication[];
+ splitTunnelingApplications: IWindowsApplication[];
}
const initialState: ISettingsReduxState = {
diff --git a/gui/src/shared/application-types.ts b/gui/src/shared/application-types.ts
index 07cea190e3..5f9fb80fd0 100644
--- a/gui/src/shared/application-types.ts
+++ b/gui/src/shared/application-types.ts
@@ -6,6 +6,10 @@ export interface IApplication {
icon?: string;
}
+export interface IWindowsApplication extends IApplication {
+ deletable: boolean;
+}
+
export interface ILinuxApplication extends IApplication {
exec: string;
type: string;
diff --git a/gui/src/shared/ipc-schema.ts b/gui/src/shared/ipc-schema.ts
index 29d00bb1c4..79a54a39ba 100644
--- a/gui/src/shared/ipc-schema.ts
+++ b/gui/src/shared/ipc-schema.ts
@@ -1,5 +1,5 @@
import { GetTextTranslations } from 'gettext-parser';
-import { IApplication, ILinuxSplitTunnelingApplication } from './application-types';
+import { IWindowsApplication, ILinuxSplitTunnelingApplication } from './application-types';
import {
AccountToken,
BridgeSettings,
@@ -60,7 +60,7 @@ export interface IAppStateSnapshot {
upgradeVersion: IAppVersionInfo;
guiSettings: IGuiSettingsState;
translations: ITranslations;
- windowsSplitTunnelingApplications?: IApplication[];
+ windowsSplitTunnelingApplications?: IWindowsApplication[];
macOsScrollbarVisibility?: MacOsScrollbarVisibility;
changelog: IChangelog;
}
@@ -201,10 +201,11 @@ export const ipcSchema = {
launchApplication: invoke<ILinuxSplitTunnelingApplication | string, LaunchApplicationResult>(),
},
windowsSplitTunneling: {
- '': notifyRenderer<IApplication[]>(),
+ '': notifyRenderer<IWindowsApplication[]>(),
setState: invoke<boolean, void>(),
- getApplications: invoke<boolean, { fromCache: boolean; applications: IApplication[] }>(),
- addApplication: invoke<IApplication | string, void>(),
- removeApplication: invoke<IApplication | string, void>(),
+ getApplications: invoke<boolean, { fromCache: boolean; applications: IWindowsApplication[] }>(),
+ addApplication: invoke<IWindowsApplication | string, void>(),
+ removeApplication: invoke<IWindowsApplication, void>(),
+ forgetManuallyAddedApplication: invoke<IWindowsApplication, void>(),
},
};