diff options
| author | Oskar Nyberg <oskar@mullvad.net> | 2022-04-20 10:53:07 +0200 |
|---|---|---|
| committer | Oskar Nyberg <oskar@mullvad.net> | 2022-04-20 10:53:07 +0200 |
| commit | 4c93bdcf07af75c8ab3a91ddb0ab2641d277b7c9 (patch) | |
| tree | b0af816c5202d64b54d3f3b91b2da700399c019e /gui/src/renderer | |
| parent | fbdf3932b450ab65940f9f006db05cd35ff10704 (diff) | |
| parent | 87a7d264343b356d1acc746635346021fe7d3c6a (diff) | |
| download | mullvadvpn-4c93bdcf07af75c8ab3a91ddb0ab2641d277b7c9.tar.xz mullvadvpn-4c93bdcf07af75c8ab3a91ddb0ab2641d277b7c9.zip | |
Merge branch 'remember-location'
Diffstat (limited to 'gui/src/renderer')
| -rw-r--r-- | gui/src/renderer/app.tsx | 41 | ||||
| -rw-r--r-- | gui/src/renderer/components/AppRouter.tsx | 8 | ||||
| -rw-r--r-- | gui/src/renderer/components/NavigationBar.tsx | 23 | ||||
| -rw-r--r-- | gui/src/renderer/lib/history.tsx | 18 | ||||
| -rw-r--r-- | gui/src/renderer/redux/userinterface/actions.ts | 16 | ||||
| -rw-r--r-- | gui/src/renderer/redux/userinterface/reducers.ts | 3 |
6 files changed, 95 insertions, 14 deletions
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, |
