summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorOskar Nyberg <oskar@mullvad.net>2022-04-20 10:53:07 +0200
committerOskar Nyberg <oskar@mullvad.net>2022-04-20 10:53:07 +0200
commit4c93bdcf07af75c8ab3a91ddb0ab2641d277b7c9 (patch)
treeb0af816c5202d64b54d3f3b91b2da700399c019e
parentfbdf3932b450ab65940f9f006db05cd35ff10704 (diff)
parent87a7d264343b356d1acc746635346021fe7d3c6a (diff)
downloadmullvadvpn-4c93bdcf07af75c8ab3a91ddb0ab2641d277b7c9.tar.xz
mullvadvpn-4c93bdcf07af75c8ab3a91ddb0ab2641d277b7c9.zip
Merge branch 'remember-location'
-rw-r--r--CHANGELOG.md1
-rw-r--r--gui/src/main/index.ts21
-rw-r--r--gui/src/main/window-controller.ts6
-rw-r--r--gui/src/renderer/app.tsx41
-rw-r--r--gui/src/renderer/components/AppRouter.tsx8
-rw-r--r--gui/src/renderer/components/NavigationBar.tsx23
-rw-r--r--gui/src/renderer/lib/history.tsx18
-rw-r--r--gui/src/renderer/redux/userinterface/actions.ts16
-rw-r--r--gui/src/renderer/redux/userinterface/reducers.ts3
-rw-r--r--gui/src/shared/ipc-schema.ts12
-rw-r--r--gui/src/shared/ipc-types.ts10
11 files changed, 140 insertions, 19 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index fb8fc35ee4..ee84941061 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -63,6 +63,7 @@ Line wrap the file at 100 chars. Th
- Fix "Open Mullvad VPN" tray context menu item not working after toggling unpinned window setting.
- Fix apps not always visible in split tunneling view after browsing for an app and then removing it
from the excluded applications.
+- Fix navigation resetting to main view when toggling the unpinned window setting.
### Security
#### Android
diff --git a/gui/src/main/index.ts b/gui/src/main/index.ts
index c39ccdbc5c..8acdac16a0 100644
--- a/gui/src/main/index.ts
+++ b/gui/src/main/index.ts
@@ -43,7 +43,12 @@ import {
import { messages, relayLocations } from '../shared/gettext';
import { SYSTEM_PREFERRED_LOCALE_KEY } from '../shared/gui-settings-state';
import { ITranslations, MacOsScrollbarVisibility } from '../shared/ipc-schema';
-import { IChangelog, ICurrentAppVersionInfo } from '../shared/ipc-types';
+import {
+ IChangelog,
+ ICurrentAppVersionInfo,
+ IHistoryObject,
+ ScrollPositions,
+} from '../shared/ipc-types';
import log, { ConsoleOutput, Logger } from '../shared/logging';
import { LogLevel } from '../shared/logging-types';
import {
@@ -252,6 +257,9 @@ class ApplicationMain {
private changelog?: IChangelog;
+ private navigationHistory?: IHistoryObject;
+ private scrollPositions: ScrollPositions = {};
+
public run() {
// Remove window animations to combat window flickering when opening window. Can be removed when
// this issue has been resolved: https://github.com/electron/electron/issues/12130
@@ -1273,6 +1281,8 @@ class ApplicationMain {
windowsSplitTunnelingApplications: this.windowsSplitTunnelingApplications,
macOsScrollbarVisibility: this.macOsScrollbarVisibility,
changelog: this.changelog ?? [],
+ navigationHistory: this.navigationHistory,
+ scrollPositions: this.scrollPositions,
}));
IpcMainEventChannel.settings.handleSetAllowLan((allowLan: boolean) =>
@@ -1486,6 +1496,13 @@ class ApplicationMain {
this.guiSettings.changelogDisplayedForVersion = this.currentVersion.gui;
});
+ IpcMainEventChannel.navigation.handleSetHistory((history) => {
+ this.navigationHistory = history;
+ });
+ IpcMainEventChannel.navigation.handleSetScrollPositions((scrollPositions) => {
+ this.scrollPositions = scrollPositions;
+ });
+
if (windowsSplitTunneling) {
this.guiSettings.browsedForSplitTunnelingApplications.forEach(
windowsSplitTunneling.addApplicationPathToCache,
@@ -1650,7 +1667,7 @@ class ApplicationMain {
const window = await this.createWindow();
- this.windowController.destroy();
+ this.windowController.close();
this.windowController = new WindowController(
window,
this.tray,
diff --git a/gui/src/main/window-controller.ts b/gui/src/main/window-controller.ts
index 4676dbbecb..b737f4da96 100644
--- a/gui/src/main/window-controller.ts
+++ b/gui/src/main/window-controller.ts
@@ -192,9 +192,11 @@ export default class WindowController {
this.notifyUpdateWindowShape();
}
- public destroy() {
+ public close() {
if (this.window && !this.window.isDestroyed()) {
- this.window.destroy();
+ this.window.webContents.closeDevTools();
+ this.window.closable = true;
+ this.window.close();
}
}
diff --git a/gui/src/renderer/app.tsx b/gui/src/renderer/app.tsx
index a0fb5f3266..d4672066f2 100644
--- a/gui/src/renderer/app.tsx
+++ b/gui/src/renderer/app.tsx
@@ -25,7 +25,12 @@ import {
import { messages, relayLocations } from '../shared/gettext';
import { IGuiSettingsState, SYSTEM_PREFERRED_LOCALE_KEY } from '../shared/gui-settings-state';
import { IRelayListPair, LaunchApplicationResult } from '../shared/ipc-schema';
-import { IChangelog, ICurrentAppVersionInfo } from '../shared/ipc-types';
+import {
+ IChangelog,
+ ICurrentAppVersionInfo,
+ IHistoryObject,
+ ScrollPositions,
+} from '../shared/ipc-types';
import log, { ConsoleOutput } from '../shared/logging';
import { LogLevel } from '../shared/logging-types';
import { Scheduler } from '../shared/scheduler';
@@ -208,7 +213,11 @@ export default class AppRenderer {
this.setAccountExpiry(initialState.accountData?.expiry);
this.setSettings(initialState.settings);
this.setIsPerformingPostUpgrade(initialState.isPerformingPostUpgrade);
- this.handleAccountChange({ deviceConfig: initialState.deviceConfig }, undefined);
+ this.handleAccountChange(
+ { deviceConfig: initialState.deviceConfig },
+ undefined,
+ initialState.navigationHistory !== undefined,
+ );
this.hasReceivedDeviceConfig = initialState.hasReceivedDeviceConfig;
this.setAccountHistory(initialState.accountHistory);
this.setTunnelState(initialState.tunnelState);
@@ -244,8 +253,16 @@ export default class AppRenderer {
void this.updateLocation();
- const navigationBase = this.getNavigationBase();
- this.history = new History(navigationBase);
+ this.reduxActions.userInterface.setScrollPositions(initialState.scrollPositions);
+
+ if (initialState.navigationHistory) {
+ // Set last action to POP to trigger automatic scrolling to saved coordinates.
+ initialState.navigationHistory.lastAction = 'POP';
+ this.history = History.fromSavedHistory(initialState.navigationHistory);
+ } else {
+ const navigationBase = this.getNavigationBase();
+ this.history = new History(navigationBase);
+ }
}
public renderView() {
@@ -555,6 +572,14 @@ export default class AppRenderer {
IpcRendererEventChannel.currentVersion.displayedChangelog();
};
+ public setNavigationHistory(history: IHistoryObject) {
+ IpcRendererEventChannel.navigation.setHistory(history);
+ }
+
+ public setScrollPositions(scrollPositions: ScrollPositions) {
+ IpcRendererEventChannel.navigation.setScrollPositions(scrollPositions);
+ }
+
// Make sure that the content height is correct and log if it isn't. This is mostly for debugging
// purposes since there's a bug in Electron that causes the app height to be another value than
// the one we have set.
@@ -804,7 +829,11 @@ export default class AppRenderer {
}
}
- private handleAccountChange(newDeviceEvent: IDeviceEvent, oldAccount?: string) {
+ private handleAccountChange(
+ newDeviceEvent: IDeviceEvent,
+ oldAccount?: string,
+ preventRedirectToConnect?: boolean,
+ ) {
const reduxAccount = this.reduxActions.account;
this.deviceConfig = newDeviceEvent.deviceConfig;
@@ -828,7 +857,7 @@ export default class AppRenderer {
if (this.previousLoginState === 'too many devices') {
this.resetNavigation();
- } else {
+ } else if (!preventRedirectToConnect) {
this.redirectToConnect();
}
break;
diff --git a/gui/src/renderer/components/AppRouter.tsx b/gui/src/renderer/components/AppRouter.tsx
index d061c7b9d5..18a8b51063 100644
--- a/gui/src/renderer/components/AppRouter.tsx
+++ b/gui/src/renderer/components/AppRouter.tsx
@@ -12,6 +12,7 @@ import SelectLocationPage from '../containers/SelectLocationPage';
import SettingsPage from '../containers/SettingsPage';
import SupportPage from '../containers/SupportPage';
import WireguardSettingsPage from '../containers/WireguardSettingsPage';
+import withAppContext, { IAppContext } from '../context';
import { IHistoryProps, ITransitionSpecification, transitions, withHistory } from '../lib/history';
import { RoutePath } from '../lib/routes';
import { DeviceRevokedView } from './DeviceRevokedView';
@@ -36,12 +37,12 @@ interface IAppRoutesState {
action?: Action;
}
-class AppRouter extends React.Component<IHistoryProps, IAppRoutesState> {
+class AppRouter extends React.Component<IHistoryProps & IAppContext, IAppRoutesState> {
private unobserveHistory?: () => void;
private focusRef = React.createRef<IFocusHandle>();
- constructor(props: IHistoryProps) {
+ constructor(props: IHistoryProps & IAppContext) {
super(props);
this.state = {
@@ -54,6 +55,7 @@ class AppRouter extends React.Component<IHistoryProps, IAppRoutesState> {
// React throttles updates, so it's impossible to capture the intermediate navigation without
// listening to the history directly.
this.unobserveHistory = this.props.history.listen((location, action, transition) => {
+ this.props.app.setNavigationHistory(this.props.history.asObject);
this.setState({
currentLocation: location,
transition,
@@ -114,6 +116,6 @@ class AppRouter extends React.Component<IHistoryProps, IAppRoutesState> {
};
}
-const AppRoutesWithRouter = withHistory(AppRouter);
+const AppRoutesWithRouter = withAppContext(withHistory(AppRouter));
export default AppRoutesWithRouter;
diff --git a/gui/src/renderer/components/NavigationBar.tsx b/gui/src/renderer/components/NavigationBar.tsx
index 2628f484eb..7e231646ce 100644
--- a/gui/src/renderer/components/NavigationBar.tsx
+++ b/gui/src/renderer/components/NavigationBar.tsx
@@ -1,7 +1,8 @@
-import React, { useCallback, useContext, useLayoutEffect, useRef } from 'react';
+import React, { useCallback, useContext, useEffect, useLayoutEffect, useRef } from 'react';
import { colors } from '../../config.json';
import { messages } from '../../shared/gettext';
+import { useAppContext } from '../context';
import useActions from '../lib/actionsHook';
import { useHistory } from '../lib/history';
import { useCombinedRefs } from '../lib/utilityHooks';
@@ -114,18 +115,32 @@ export const NavigationScrollbars = React.forwardRef(function NavigationScrollba
) {
const history = useHistory();
const { onScroll } = useContext(NavigationScrollContext);
+ const { setScrollPositions } = useAppContext();
const ref = useRef<CustomScrollbarsRef>();
const combinedRefs = useCombinedRefs(forwardedRef, ref);
const { addScrollPosition, removeScrollPosition } = useActions(userInterface);
- const scrollPosition = useSelector(
- (state) => state.userInterface.scrollPosition[history.location.pathname],
- );
+ const scrollPositions = useSelector((state) => state.userInterface.scrollPosition);
+
+ useEffect(() => {
+ const path = history.location.pathname;
+ const beforeunload = () => {
+ if (ref.current) {
+ const scrollPosition = ref.current.getScrollPosition();
+ setScrollPositions({ ...scrollPositions, [path]: scrollPosition });
+ }
+ };
+
+ window.addEventListener('beforeunload', beforeunload);
+
+ return () => window.removeEventListener('beforeunload', beforeunload);
+ }, [scrollPositions]);
useLayoutEffect(() => {
const path = history.location.pathname;
+ const scrollPosition = scrollPositions[history.location.pathname];
if (history.action === 'POP' && scrollPosition) {
ref.current?.scrollTo(...scrollPosition);
removeScrollPosition(path);
diff --git a/gui/src/renderer/lib/history.tsx b/gui/src/renderer/lib/history.tsx
index 2eb08d001d..9a0205b382 100644
--- a/gui/src/renderer/lib/history.tsx
+++ b/gui/src/renderer/lib/history.tsx
@@ -2,6 +2,7 @@ import { Action, History as OriginalHistory, Location, LocationDescriptorObject
import React from 'react';
import { RouteComponentProps, useHistory as useReactRouterHistory, withRouter } from 'react-router';
+import { IHistoryObject } from '../../shared/ipc-types';
import { GeneratedRoutePath, RoutePath } from './routes';
export interface ITransitionSpecification {
@@ -61,6 +62,15 @@ export default class History {
this.entries = [this.createLocation(location, state)];
}
+ public static fromSavedHistory(savedHistory: IHistoryObject): History {
+ const history = new History(RoutePath.launch);
+ history.entries = savedHistory.entries;
+ history.index = savedHistory.index;
+ history.lastAction = savedHistory.lastAction;
+
+ return history;
+ }
+
public get location(): Location<S> {
return this.entries[this.index];
}
@@ -126,6 +136,14 @@ export default class History {
return this as OriginalHistory;
}
+ public get asObject(): IHistoryObject {
+ return {
+ entries: this.entries,
+ index: this.index,
+ lastAction: this.lastAction,
+ };
+ }
+
public block(): never {
throw Error('Not implemented');
}
diff --git a/gui/src/renderer/redux/userinterface/actions.ts b/gui/src/renderer/redux/userinterface/actions.ts
index ce0cd5401b..cc8929934d 100644
--- a/gui/src/renderer/redux/userinterface/actions.ts
+++ b/gui/src/renderer/redux/userinterface/actions.ts
@@ -1,5 +1,5 @@
import { MacOsScrollbarVisibility } from '../../../shared/ipc-schema';
-import { IChangelog } from '../../../shared/ipc-types';
+import { IChangelog, ScrollPositions } from '../../../shared/ipc-types';
export interface IUpdateLocaleAction {
type: 'UPDATE_LOCALE';
@@ -20,6 +20,11 @@ export interface ISetWindowFocusedAction {
focused: boolean;
}
+export interface ISetScrollPositions {
+ type: 'SET_SCROLL_POSITIONS';
+ scrollPositions: ScrollPositions;
+}
+
export interface IAddScrollPosition {
type: 'ADD_SCROLL_POSITION';
path: string;
@@ -56,6 +61,7 @@ export type UserInterfaceAction =
| IUpdateWindowArrowPositionAction
| IUpdateConnectionInfoOpenAction
| ISetWindowFocusedAction
+ | ISetScrollPositions
| IAddScrollPosition
| IRemoveScrollPosition
| ISetMacOsScrollbarVisibility
@@ -90,6 +96,13 @@ function setWindowFocused(focused: boolean): ISetWindowFocusedAction {
};
}
+function setScrollPositions(scrollPositions: ScrollPositions): ISetScrollPositions {
+ return {
+ type: 'SET_SCROLL_POSITIONS',
+ scrollPositions,
+ };
+}
+
function addScrollPosition(path: string, scrollPosition: [number, number]): IAddScrollPosition {
return {
type: 'ADD_SCROLL_POSITION',
@@ -140,6 +153,7 @@ export default {
updateWindowArrowPosition,
toggleConnectionPanel,
setWindowFocused,
+ setScrollPositions,
addScrollPosition,
removeScrollPosition,
setMacOsScrollbarVisibility,
diff --git a/gui/src/renderer/redux/userinterface/reducers.ts b/gui/src/renderer/redux/userinterface/reducers.ts
index ca470a8cba..1850d553ab 100644
--- a/gui/src/renderer/redux/userinterface/reducers.ts
+++ b/gui/src/renderer/redux/userinterface/reducers.ts
@@ -42,6 +42,9 @@ export default function (
case 'SET_WINDOW_FOCUSED':
return { ...state, windowFocused: action.focused };
+ case 'SET_SCROLL_POSITIONS':
+ return { ...state, scrollPosition: action.scrollPositions };
+
case 'ADD_SCROLL_POSITION':
return {
...state,
diff --git a/gui/src/shared/ipc-schema.ts b/gui/src/shared/ipc-schema.ts
index ce517c8f21..e09d552c25 100644
--- a/gui/src/shared/ipc-schema.ts
+++ b/gui/src/shared/ipc-schema.ts
@@ -27,7 +27,13 @@ interface ILogEntry {
message: string;
}
import { invoke, invokeSync, notifyRenderer, send } from './ipc-helpers';
-import { IChangelog, ICurrentAppVersionInfo, IWindowShapeParameters } from './ipc-types';
+import {
+ IChangelog,
+ ICurrentAppVersionInfo,
+ IHistoryObject,
+ IWindowShapeParameters,
+ ScrollPositions,
+} from './ipc-types';
export interface ITranslations {
locale: string;
@@ -66,6 +72,8 @@ export interface IAppStateSnapshot {
windowsSplitTunnelingApplications?: IWindowsApplication[];
macOsScrollbarVisibility?: MacOsScrollbarVisibility;
changelog: IChangelog;
+ navigationHistory?: IHistoryObject;
+ scrollPositions: ScrollPositions;
}
// The different types of requests are:
@@ -118,6 +126,8 @@ export const ipcSchema = {
},
navigation: {
reset: notifyRenderer<void>(),
+ setHistory: send<IHistoryObject>(),
+ setScrollPositions: send<ScrollPositions>(),
},
daemon: {
isPerformingPostUpgrade: notifyRenderer<boolean>(),
diff --git a/gui/src/shared/ipc-types.ts b/gui/src/shared/ipc-types.ts
index 0624ac95cd..2b42f42b0e 100644
--- a/gui/src/shared/ipc-types.ts
+++ b/gui/src/shared/ipc-types.ts
@@ -1,3 +1,5 @@
+import { Action, Location } from 'history';
+
export interface ICurrentAppVersionInfo {
gui: string;
daemon?: string;
@@ -10,3 +12,11 @@ export interface IWindowShapeParameters {
}
export type IChangelog = Array<string>;
+
+export interface IHistoryObject {
+ entries: Location<unknown>[];
+ index: number;
+ lastAction: Action;
+}
+
+export type ScrollPositions = Record<string, [number, number]>;