summaryrefslogtreecommitdiffhomepage
path: root/gui/src
diff options
context:
space:
mode:
authorOskar Nyberg <oskar@mullvad.net>2020-10-13 13:55:24 +0200
committerOskar Nyberg <oskar@mullvad.net>2020-10-13 13:55:24 +0200
commitb1e1fd6d444bba2192c372f7a23242fe2daebfdc (patch)
treeabd6372656743f188dd1d2179aad43896b7d1574 /gui/src
parentba755eba2604ff8dcda0f84272c30a2fdc9b2a51 (diff)
parentbecdea43225f6960fd9999b76afdb681fdaf80da (diff)
downloadmullvadvpn-b1e1fd6d444bba2192c372f7a23242fe2daebfdc.tar.xz
mullvadvpn-b1e1fd6d444bba2192c372f7a23242fe2daebfdc.zip
Merge branch 'remember-scroll-position' into master
Diffstat (limited to 'gui/src')
-rw-r--r--gui/src/renderer/components/NavigationBar.tsx43
-rw-r--r--gui/src/renderer/lib/utilityHooks.ts14
-rw-r--r--gui/src/renderer/redux/userinterface/actions.ts32
-rw-r--r--gui/src/renderer/redux/userinterface/reducers.ts14
4 files changed, 98 insertions, 5 deletions
diff --git a/gui/src/renderer/components/NavigationBar.tsx b/gui/src/renderer/components/NavigationBar.tsx
index 01d42bb8ba..cc1a02de50 100644
--- a/gui/src/renderer/components/NavigationBar.tsx
+++ b/gui/src/renderer/components/NavigationBar.tsx
@@ -1,6 +1,19 @@
-import React, { useCallback, useContext, useLayoutEffect, useRef, useState } from 'react';
+import React, {
+ useCallback,
+ useContext,
+ useEffect,
+ useLayoutEffect,
+ useRef,
+ useState,
+} from 'react';
+import { useSelector } from 'react-redux';
+import { useHistory } from 'react-router';
import { colors } from '../../config.json';
import { messages } from '../../shared/gettext';
+import useActions from '../lib/actionsHook';
+import { useCombinedRefs } from '../lib/utilityHooks';
+import { IReduxState } from '../redux/store';
+import userInterface from '../redux/userinterface/actions';
import CustomScrollbars, { IScrollEvent } from './CustomScrollbars';
import {
StyledBackBarItemButton,
@@ -110,10 +123,34 @@ interface INavigationScrollbarsProps {
export const NavigationScrollbars = React.forwardRef(function NavigationScrollbarsT(
props: INavigationScrollbarsProps,
- ref?: React.Ref<CustomScrollbars>,
+ forwardedRef?: React.Ref<CustomScrollbars>,
) {
+ const history = useHistory();
const { onScroll } = useContext(NavigationScrollContext);
+ const ref = useRef<CustomScrollbars>();
+ const combinedRefs = useCombinedRefs(forwardedRef, ref);
+
+ const { addScrollPosition, removeScrollPosition } = useActions(userInterface);
+ const scrollPosition = useSelector(
+ (state: IReduxState) => state.userInterface.scrollPosition[history.location.pathname],
+ );
+
+ useEffect(() => {
+ const path = history.location.pathname;
+
+ if (history.action === 'POP' && scrollPosition) {
+ ref.current?.scrollTo(...scrollPosition);
+ removeScrollPosition(path);
+ }
+
+ return () => {
+ if (history.action === 'PUSH' && ref.current) {
+ addScrollPosition(path, ref.current.getScrollPosition());
+ }
+ };
+ }, []);
+
const handleScroll = useCallback((event: IScrollEvent) => {
onScroll(event);
props.onScroll?.(event);
@@ -121,7 +158,7 @@ export const NavigationScrollbars = React.forwardRef(function NavigationScrollba
return (
<CustomScrollbars
- ref={ref}
+ ref={combinedRefs}
className={props.className}
fillContainer={props.fillContainer}
onScroll={handleScroll}>
diff --git a/gui/src/renderer/lib/utilityHooks.ts b/gui/src/renderer/lib/utilityHooks.ts
index cbb5575f9b..4156c546ea 100644
--- a/gui/src/renderer/lib/utilityHooks.ts
+++ b/gui/src/renderer/lib/utilityHooks.ts
@@ -1,4 +1,4 @@
-import { useCallback, useEffect, useRef } from 'react';
+import React, { useCallback, useEffect, useRef } from 'react';
export function useMounted() {
const mountedRef = useRef(false);
@@ -13,3 +13,15 @@ export function useMounted() {
return isMounted;
}
+
+export function useCombinedRefs<T>(...refs: (React.Ref<T> | undefined)[]): React.RefCallback<T> {
+ return useCallback((element: T | null) => refs.forEach((ref) => assignToRef(element, ref)), []);
+}
+
+function assignToRef<T>(element: T | null, ref?: React.Ref<T>) {
+ if (typeof ref === 'function') {
+ ref(element);
+ } else if (ref && element) {
+ (ref as React.MutableRefObject<T>).current = element;
+ }
+}
diff --git a/gui/src/renderer/redux/userinterface/actions.ts b/gui/src/renderer/redux/userinterface/actions.ts
index affd11798d..d0d1378a88 100644
--- a/gui/src/renderer/redux/userinterface/actions.ts
+++ b/gui/src/renderer/redux/userinterface/actions.ts
@@ -24,12 +24,25 @@ export interface ISetWindowFocusedAction {
focused: boolean;
}
+export interface IAddScrollPosition {
+ type: 'ADD_SCROLL_POSITION';
+ path: string;
+ scrollPosition: [number, number];
+}
+
+export interface IRemoveScrollPosition {
+ type: 'REMOVE_SCROLL_POSITION';
+ path: string;
+}
+
export type UserInterfaceAction =
| IUpdateLocaleAction
| IUpdateWindowArrowPositionAction
| IUpdateConnectionInfoOpenAction
| ISetLocationScopeAction
- | ISetWindowFocusedAction;
+ | ISetWindowFocusedAction
+ | IAddScrollPosition
+ | IRemoveScrollPosition;
function updateLocale(locale: string): IUpdateLocaleAction {
return {
@@ -65,10 +78,27 @@ function setWindowFocused(focused: boolean): ISetWindowFocusedAction {
};
}
+function addScrollPosition(path: string, scrollPosition: [number, number]): IAddScrollPosition {
+ return {
+ type: 'ADD_SCROLL_POSITION',
+ path,
+ scrollPosition,
+ };
+}
+
+function removeScrollPosition(path: string): IRemoveScrollPosition {
+ return {
+ type: 'REMOVE_SCROLL_POSITION',
+ path,
+ };
+}
+
export default {
updateLocale,
updateWindowArrowPosition,
toggleConnectionPanel,
setLocationScope,
setWindowFocused,
+ addScrollPosition,
+ removeScrollPosition,
};
diff --git a/gui/src/renderer/redux/userinterface/reducers.ts b/gui/src/renderer/redux/userinterface/reducers.ts
index 4fef8efbfb..05e091797e 100644
--- a/gui/src/renderer/redux/userinterface/reducers.ts
+++ b/gui/src/renderer/redux/userinterface/reducers.ts
@@ -11,6 +11,7 @@ export interface IUserInterfaceReduxState {
connectionPanelVisible: boolean;
locationScope: LocationScope;
windowFocused: boolean;
+ scrollPosition: Record<string, [number, number]>;
}
const initialState: IUserInterfaceReduxState = {
@@ -18,6 +19,7 @@ const initialState: IUserInterfaceReduxState = {
connectionPanelVisible: false,
locationScope: LocationScope.relay,
windowFocused: false,
+ scrollPosition: {},
};
export default function (
@@ -40,6 +42,18 @@ export default function (
case 'SET_WINDOW_FOCUSED':
return { ...state, windowFocused: action.focused };
+ case 'ADD_SCROLL_POSITION':
+ return {
+ ...state,
+ scrollPosition: { ...state.scrollPosition, [action.path]: action.scrollPosition },
+ };
+
+ case 'REMOVE_SCROLL_POSITION': {
+ const scrollPosition = { ...state.scrollPosition };
+ delete scrollPosition[action.path];
+ return { ...state, scrollPosition };
+ }
+
default:
return state;
}