diff options
| author | Oskar Nyberg <oskar@mullvad.net> | 2022-03-30 09:23:28 +0200 |
|---|---|---|
| committer | Oskar Nyberg <oskar@mullvad.net> | 2022-03-31 15:59:47 +0200 |
| commit | a7d0e178604e740c695003ab2c99ef0216a516d2 (patch) | |
| tree | 48ad63e293336314bc7777e1ad15c7ad52592079 /gui/src | |
| parent | 296277eb75a58eff48addf31a7e8f4cf4d7ab4fd (diff) | |
| download | mullvadvpn-a7d0e178604e740c695003ab2c99ef0216a516d2.tar.xz mullvadvpn-a7d0e178604e740c695003ab2c99ef0216a516d2.zip | |
Add button to delete browsed for split tunneling apps
Diffstat (limited to 'gui/src')
| -rw-r--r-- | gui/src/main/gui-settings.ts | 9 | ||||
| -rw-r--r-- | gui/src/main/index.ts | 70 | ||||
| -rw-r--r-- | gui/src/main/windows-split-tunneling.ts | 26 | ||||
| -rw-r--r-- | gui/src/renderer/app.tsx | 14 | ||||
| -rw-r--r-- | gui/src/renderer/components/SplitTunnelingSettings.tsx | 111 | ||||
| -rw-r--r-- | gui/src/renderer/components/SplitTunnelingSettingsStyles.tsx | 2 | ||||
| -rw-r--r-- | gui/src/renderer/redux/settings/actions.ts | 6 | ||||
| -rw-r--r-- | gui/src/renderer/redux/settings/reducers.ts | 4 | ||||
| -rw-r--r-- | gui/src/shared/application-types.ts | 4 | ||||
| -rw-r--r-- | gui/src/shared/ipc-schema.ts | 13 |
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>(), }, }; |
