From 8c93b3c5b3be04b43d32c52301e673d27a384cc9 Mon Sep 17 00:00:00 2001 From: ran Date: Fri, 12 Jan 2024 23:29:03 +0800 Subject: [PATCH] Modify logic for all preferences --- client/src/app.tsx | 31 ++++++++----- client/src/components/infoview/context.ts | 17 +++++-- client/src/components/popup/preferences.tsx | 51 +++++++++++---------- client/src/components/welcome.tsx | 8 ++-- client/src/components/world_tree.tsx | 5 +- client/src/css/welcome.css | 12 +++++ client/src/hooks.ts | 24 ---------- client/src/state/local_storage.ts | 9 ++++ client/src/state/preferences.ts | 27 ++++++----- client/src/state/store.ts | 6 ++- 10 files changed, 107 insertions(+), 83 deletions(-) diff --git a/client/src/app.tsx b/client/src/app.tsx index 871c226..d5b8647 100644 --- a/client/src/app.tsx +++ b/client/src/app.tsx @@ -8,39 +8,48 @@ import '@fontsource/roboto/700.css'; import './css/reset.css'; import './css/app.css'; -import { MobileContext } from './components/infoview/context'; -import { useMobile } from './hooks'; -import { AUTO_SWITCH_THRESHOLD, getWindowDimensions} from './state/preferences'; +import { MobileContext, PreferencesContext} from './components/infoview/context'; +import { AUTO_SWITCH_THRESHOLD, getWindowDimensions, setLayout, setisSavePreferences, PreferencesState} from './state/preferences'; +import { useAppDispatch, useAppSelector } from './hooks'; export const GameIdContext = React.createContext(undefined); function App() { - const { mobile, setMobile, lockMobile, setLockMobile } = useMobile(); + const dispatch = useAppDispatch() const params = useParams() const gameId = "g/" + params.owner + "/" + params.repo + // TODO: + const [mobile, setMobile] = React.useState() + const layout = useAppSelector((state) => state.preferences.layout); + const changeLayout = (layout: PreferencesState["layout"]) => dispatch(setLayout(layout)) + const isSavePreferences = useAppSelector((state) => state.preferences.isSavePreferences); + const changeIsSavePreferences = (isSave: boolean) => dispatch(setisSavePreferences(isSave)) + const automaticallyAdjustLayout = () => { const {width} = getWindowDimensions() setMobile(width < AUTO_SWITCH_THRESHOLD) } React.useEffect(()=>{ - if (!lockMobile){ + if (layout === "auto"){ void automaticallyAdjustLayout() window.addEventListener('resize', automaticallyAdjustLayout) - return () => { - window.removeEventListener('resize', automaticallyAdjustLayout) - } + return () => window.removeEventListener('resize', automaticallyAdjustLayout) + } else { + setMobile(layout === "mobile") } - }, [lockMobile]) + }, [layout]) return (
- - + + + +
diff --git a/client/src/components/infoview/context.ts b/client/src/components/infoview/context.ts index 8a62b36..948473b 100644 --- a/client/src/components/infoview/context.ts +++ b/client/src/components/infoview/context.ts @@ -5,6 +5,7 @@ import * as React from 'react'; import * as monaco from 'monaco-editor/esm/vs/editor/editor.api.js' import { InteractiveDiagnostic, InteractiveTermGoal } from '@leanprover/infoview-api'; import { GameHint, InteractiveGoal, InteractiveGoals } from './rpc_api'; +import { PreferencesState } from '../../state/preferences'; export const MonacoEditorContext = React.createContext( null as any) @@ -62,18 +63,26 @@ export const ProofStateContext = React.createContext<{ setProofState: () => {}, }) +export interface IPreferencesContext extends PreferencesState{ + setLayout: React.Dispatch>; + setIsSavePreferences: React.Dispatch>; +} + +export const PreferencesContext = React.createContext({ + layout: "auto", + isSavePreferences: false, + setLayout: () => {}, + setIsSavePreferences: () => {} +}) + export interface IMobileContext { mobile : boolean, setMobile: React.Dispatch>, - lockMobile: boolean, - setLockMobile: React.Dispatch>, } export const MobileContext = React.createContext({ mobile: false, setMobile: () => {}, - lockMobile: false, - setLockMobile: () => {} }) export const WorldLevelIdContext = React.createContext<{ diff --git a/client/src/components/popup/preferences.tsx b/client/src/components/popup/preferences.tsx index 73b2dbe..fdda8e6 100644 --- a/client/src/components/popup/preferences.tsx +++ b/client/src/components/popup/preferences.tsx @@ -1,16 +1,20 @@ import * as React from 'react' import { Input, Typography } from '@mui/material' import Markdown from '../markdown' -import Switch from '@mui/material/Switch'; +import { Switch, Button, ButtonGroup } from '@mui/material'; + import FormControlLabel from '@mui/material/FormControlLabel'; import { IMobileContext } from "../infoview/context" +import { PreferencesState } from "../../state/preferences" -interface PreferencesPopupProps extends IMobileContext{ - handleClose: () => void -} +interface PreferencesPopupProps extends PreferencesState{ + handleClose: () => void, + setLayout: (layout: "mobile" | "auto" | "desktop") => void, + setIsSavePreferences: (isSave: boolean) => void +} -export function PreferencesPopup({ mobile, setMobile, lockMobile, setLockMobile, handleClose }: PreferencesPopupProps) { +export function PreferencesPopup({ layout, setLayout, isSavePreferences, setIsSavePreferences, handleClose }: PreferencesPopupProps) { return
@@ -18,34 +22,35 @@ export function PreferencesPopup({ mobile, setMobile, lockMobile, setLockMobile,
-

Mobile layout

+

Layout

-
+
setMobile(!mobile)} - name="checked" - color="primary" - /> + + + + + } - label="Enable" - labelPlacement="start" + label="" />
+
+ +
setLockMobile(!lockMobile)} - name="checked" - color="primary" - /> + setIsSavePreferences(!isSavePreferences)} + name="checked" + color="primary" + /> } - label="Auto" - labelPlacement="start" + label="Save my settings (in the browser store)" + labelPlacement="end" />
diff --git a/client/src/components/welcome.tsx b/client/src/components/welcome.tsx index 89b9adc..18f9f9d 100644 --- a/client/src/components/welcome.tsx +++ b/client/src/components/welcome.tsx @@ -10,7 +10,7 @@ import { useAppDispatch, useAppSelector } from '../hooks' import { changedOpenedIntro, selectOpenedIntro } from '../state/progress' import { useGetGameInfoQuery, useLoadInventoryOverviewQuery } from '../state/api' import { Button } from './button' -import { MobileContext } from './infoview/context' +import { MobileContext, PreferencesContext } from './infoview/context' import { InventoryPanel } from './inventory' import { ErasePopup } from './popup/erase' import { InfoPopup } from './popup/game_info' @@ -64,7 +64,9 @@ function IntroductionPanel({introduction, setPageNumber}: {introduction: string, /** main page of the game showing among others the tree of worlds/levels */ function Welcome() { const gameId = React.useContext(GameIdContext) - const {mobile, setMobile, lockMobile, setLockMobile} = React.useContext(MobileContext) + const {mobile, setMobile} = React.useContext(MobileContext) + const {layout, isSavePreferences, setLayout, setIsSavePreferences} = React.useContext(PreferencesContext) + const gameInfo = useGetGameInfoQuery({game: gameId}) const inventory = useLoadInventoryOverviewQuery({game: gameId}) @@ -134,7 +136,7 @@ function Welcome() { {eraseMenu? : null} {uploadMenu? : null} {info ? : null} - {preferencesPopup ? : null} + {preferencesPopup ? : null} } diff --git a/client/src/components/world_tree.tsx b/client/src/components/world_tree.tsx index 1aa495b..76cf5fb 100644 --- a/client/src/components/world_tree.tsx +++ b/client/src/components/world_tree.tsx @@ -11,11 +11,12 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { faXmark, faCircleQuestion } from '@fortawesome/free-solid-svg-icons' import { GameIdContext } from '../app' -import { useAppDispatch, useMobile } from '../hooks' +import { useAppDispatch } from '../hooks' import { selectDifficulty, changedDifficulty, selectCompleted } from '../state/progress' import { store } from '../state/store' import '../css/world_tree.css' +import { MobileContext } from './infoview/context' // Settings for the world tree cytoscape.use( klay ) @@ -197,7 +198,7 @@ export function WorldSelectionMenu({rulesHelp, setRulesHelp}) { const gameId = React.useContext(GameIdContext) const difficulty = useSelector(selectDifficulty(gameId)) const dispatch = useAppDispatch() - const { mobile } = useMobile() + const { mobile } = React.useContext(MobileContext) function label(x : number) { diff --git a/client/src/css/welcome.css b/client/src/css/welcome.css index 94e17e3..387bc48 100644 --- a/client/src/css/welcome.css +++ b/client/src/css/welcome.css @@ -187,3 +187,15 @@ h5, h6 { margin-left: 0.3rem; margin-right: 0.3rem; } + +.preferences-category.tail-category{ + margin-top: 2em; +} + +.preferences-item.first{ + margin-top: 1em; +} + +.preferences-item.leave-left-gap{ + margin-left: 1em; +} \ No newline at end of file diff --git a/client/src/hooks.ts b/client/src/hooks.ts index 0ac6e2f..afdce0a 100644 --- a/client/src/hooks.ts +++ b/client/src/hooks.ts @@ -1,30 +1,6 @@ import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux' import type { RootState, AppDispatch } from './state/store' -import { setMobile as setMobileState, setLockMobile as setLockMobileState} from "./state/preferences" - // Use throughout your app instead of plain `useDispatch` and `useSelector` export const useAppDispatch: () => AppDispatch = useDispatch export const useAppSelector: TypedUseSelectorHook = useSelector - -export const useMobile = () => { - const dispatch = useAppDispatch(); - - const mobile = useAppSelector((state) => state.preferences.mobile); - const lockMobile = useAppSelector((state) => state.preferences.lockMobile); - - const setMobile = (val: boolean) => { - dispatch(setMobileState(val)); - }; - - const setLockMobile = (val: boolean) => { - dispatch(setLockMobileState(val)); - }; - - return { - mobile, - setMobile, - lockMobile, - setLockMobile, - }; -}; diff --git a/client/src/state/local_storage.ts b/client/src/state/local_storage.ts index ea165ce..098768e 100644 --- a/client/src/state/local_storage.ts +++ b/client/src/state/local_storage.ts @@ -57,3 +57,12 @@ export function savePreferences(state: any) { // Ignore } } + +export function removePreferences() { + try { + localStorage.removeItem(PREFERENCES_KEY); + } catch (e) { + // Ignore + } +} + diff --git a/client/src/state/preferences.ts b/client/src/state/preferences.ts index 6ee0fa1..b00dd71 100644 --- a/client/src/state/preferences.ts +++ b/client/src/state/preferences.ts @@ -1,10 +1,10 @@ import { createSlice } from "@reduxjs/toolkit"; -import { loadPreferences } from "./local_storage"; +import { loadPreferences, removePreferences, savePreferences } from "./local_storage"; -interface PreferencesState { - mobile: boolean; - lockMobile: boolean; +export interface PreferencesState { + layout: "mobile" | "auto" | "desktop"; + isSavePreferences: boolean; } export function getWindowDimensions() { @@ -12,26 +12,25 @@ export function getWindowDimensions() { return {width, height} } -const { width } = getWindowDimensions() - export const AUTO_SWITCH_THRESHOLD = 800 -const initialState: PreferencesState = loadPreferences() ?? { - mobile: width < AUTO_SWITCH_THRESHOLD, - lockMobile: false +const initialState: PreferencesState = loadPreferences() ??{ + layout: "auto", + isSavePreferences: false } export const preferencesSlice = createSlice({ name: "preferences", initialState, reducers: { - setMobile: (state, action) => { - state.mobile = action.payload; + setLayout: (state, action) => { + state.layout = action.payload; }, - setLockMobile: (state, action) => { - state.lockMobile = action.payload; + setisSavePreferences: (state, action) => { + state.isSavePreferences = action.payload; + action.payload ? savePreferences(state) : removePreferences() }, }, }); -export const { setMobile, setLockMobile } = preferencesSlice.actions; +export const { setLayout, setisSavePreferences } = preferencesSlice.actions; diff --git a/client/src/state/store.ts b/client/src/state/store.ts index 4406c84..cdda9c5 100644 --- a/client/src/state/store.ts +++ b/client/src/state/store.ts @@ -8,7 +8,7 @@ import { connection } from '../connection' import { apiSlice } from './api' import { progressSlice } from './progress' import { preferencesSlice } from "./preferences" -import { saveState, savePreferences } from "./local_storage"; +import { saveState, savePreferences, removePreferences} from "./local_storage"; export const store = configureStore({ @@ -29,7 +29,9 @@ export const store = configureStore({ store.subscribe( debounce(() => { saveState(store.getState()[progressSlice.name]); - savePreferences(store.getState()[preferencesSlice.name]); + + const preferencesState= store.getState()[preferencesSlice.name] + preferencesState.isSavePreferences ? savePreferences(preferencesState) : removePreferences() }, 800) );