diff options
| author | Oskar Nyberg <oskar@mullvad.net> | 2020-10-13 13:55:24 +0200 |
|---|---|---|
| committer | Oskar Nyberg <oskar@mullvad.net> | 2020-10-13 13:55:24 +0200 |
| commit | b1e1fd6d444bba2192c372f7a23242fe2daebfdc (patch) | |
| tree | abd6372656743f188dd1d2179aad43896b7d1574 /gui/src | |
| parent | ba755eba2604ff8dcda0f84272c30a2fdc9b2a51 (diff) | |
| parent | becdea43225f6960fd9999b76afdb681fdaf80da (diff) | |
| download | mullvadvpn-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.tsx | 43 | ||||
| -rw-r--r-- | gui/src/renderer/lib/utilityHooks.ts | 14 | ||||
| -rw-r--r-- | gui/src/renderer/redux/userinterface/actions.ts | 32 | ||||
| -rw-r--r-- | gui/src/renderer/redux/userinterface/reducers.ts | 14 |
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; } |
