From ee850dec717cfb75e086058c449f032923b3ebbf Mon Sep 17 00:00:00 2001 From: David Lechner Date: Mon, 5 Dec 2022 13:57:45 -0600 Subject: [PATCH] activities: use sessionStorage for selected tab The useLocalStorage hook synchronizes state between windows. In the case of the activities tabs, we don't want this state synchronized, otherwise changing a tab in one window would change the tab in all open windows. Instead, we can use sessionStorage so that the state persists when refreshing or duplicating a browser tab. Local storage is still used as the default value so that any new window that is opened will use the last selected state. Issue: https://github.com/pybricks/support/issues/807 --- CHANGELOG.md | 2 ++ src/activities/Activities.test.tsx | 1 + src/activities/hooks.ts | 29 ++++++++++++++++++++++++++--- src/app/App.test.tsx | 1 + 4 files changed, 30 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e5973944..c2fccd6a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,9 @@ ### Fixed - Fixed first tour item not shown if settings is not open ([support#823]). +- Fixed selected activity tab not controlled independently per window ([support#807]); +[support#807]: https://github.com/pybricks/support/issues/807 [support#823]: https://github.com/pybricks/support/issues/823 ## [2.0.0-beta.12] - 2022-12-02 diff --git a/src/activities/Activities.test.tsx b/src/activities/Activities.test.tsx index 315fbeb0..0074bfe9 100644 --- a/src/activities/Activities.test.tsx +++ b/src/activities/Activities.test.tsx @@ -11,6 +11,7 @@ afterEach(() => { cleanup(); jest.resetAllMocks(); localStorage.clear(); + sessionStorage.clear(); }); describe('Activities', () => { diff --git a/src/activities/hooks.ts b/src/activities/hooks.ts index 86e4fc98..955e4256 100644 --- a/src/activities/hooks.ts +++ b/src/activities/hooks.ts @@ -1,7 +1,8 @@ // SPDX-License-Identifier: MIT // Copyright (c) 2022 The Pybricks Authors -import { useLocalStorage } from 'usehooks-ts'; +import { useEffect } from 'react'; +import { useEffectOnce, useLocalStorage, useSessionStorage } from 'usehooks-ts'; /** Indicates the selected activity. */ export enum Activity { @@ -17,6 +18,28 @@ export enum Activity { * React hook for getting and setting the selected activity in the activity panel. * @returns a tuple of the current state and the setter function (like useState()). */ -export function useActivitiesSelectedActivity() { - return useLocalStorage('activities.selectedActivity', Activity.Explorer); +export function useActivitiesSelectedActivity(): ReturnType< + typeof useSessionStorage +> { + // If multiple windows are open, this allows each window to control the + // current activity independently. The local storage uses a "last one wins" + // approach to deciding which state to restore when a window is opened. + const [lastSelectedActivity, setLastSelectedActivity] = useLocalStorage( + 'activities.selectedActivity', + Activity.Explorer, + ); + + const [selectedActivity, setSelectedActivity] = useSessionStorage( + 'activities.selectedActivity', + lastSelectedActivity, + ); + + // Force writing to session storage since default value is not constant. + useEffectOnce(() => setSelectedActivity(selectedActivity)); + + useEffect(() => { + setLastSelectedActivity(selectedActivity); + }, [selectedActivity]); + + return [selectedActivity, setSelectedActivity]; } diff --git a/src/app/App.test.tsx b/src/app/App.test.tsx index 02be2edc..f8891854 100644 --- a/src/app/App.test.tsx +++ b/src/app/App.test.tsx @@ -18,6 +18,7 @@ afterEach(() => { cleanup(); jest.resetAllMocks(); localStorage.clear(); + sessionStorage.clear(); }); it.each([false, true])('should render', (darkMode) => {