Modify logic for all preferences
This commit is contained in:
+20
-11
@@ -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<string>(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<boolean>()
|
||||
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 (
|
||||
<div className="app">
|
||||
<GameIdContext.Provider value={gameId}>
|
||||
<MobileContext.Provider value={{mobile, setMobile, lockMobile, setLockMobile}}>
|
||||
<Outlet />
|
||||
<MobileContext.Provider value={{mobile, setMobile}}>
|
||||
<PreferencesContext.Provider value={{layout, isSavePreferences, setLayout: changeLayout, setIsSavePreferences: changeIsSavePreferences}}>
|
||||
<Outlet />
|
||||
</PreferencesContext.Provider>
|
||||
</MobileContext.Provider>
|
||||
</GameIdContext.Provider>
|
||||
</div>
|
||||
|
||||
@@ -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<monaco.editor.IStandaloneCodeEditor>(
|
||||
null as any)
|
||||
@@ -62,18 +63,26 @@ export const ProofStateContext = React.createContext<{
|
||||
setProofState: () => {},
|
||||
})
|
||||
|
||||
export interface IPreferencesContext extends PreferencesState{
|
||||
setLayout: React.Dispatch<React.SetStateAction<PreferencesState["layout"]>>;
|
||||
setIsSavePreferences: React.Dispatch<React.SetStateAction<Boolean>>;
|
||||
}
|
||||
|
||||
export const PreferencesContext = React.createContext<IPreferencesContext>({
|
||||
layout: "auto",
|
||||
isSavePreferences: false,
|
||||
setLayout: () => {},
|
||||
setIsSavePreferences: () => {}
|
||||
})
|
||||
|
||||
export interface IMobileContext {
|
||||
mobile : boolean,
|
||||
setMobile: React.Dispatch<React.SetStateAction<Boolean>>,
|
||||
lockMobile: boolean,
|
||||
setLockMobile: React.Dispatch<React.SetStateAction<Boolean>>,
|
||||
}
|
||||
|
||||
export const MobileContext = React.createContext<IMobileContext>({
|
||||
mobile: false,
|
||||
setMobile: () => {},
|
||||
lockMobile: false,
|
||||
setLockMobile: () => {}
|
||||
})
|
||||
|
||||
export const WorldLevelIdContext = React.createContext<{
|
||||
|
||||
@@ -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 <div className="modal-wrapper">
|
||||
<div className="modal-backdrop" onClick={handleClose} />
|
||||
<div className="modal">
|
||||
@@ -18,34 +22,35 @@ export function PreferencesPopup({ mobile, setMobile, lockMobile, setLockMobile,
|
||||
<Typography variant="body1" component="div" className="settings">
|
||||
<div className='preferences-category'>
|
||||
<div className='category-title'>
|
||||
<h3>Mobile layout</h3>
|
||||
<h3>Layout</h3>
|
||||
</div>
|
||||
<div className='preferences-item'>
|
||||
<div className='preferences-item first leave-left-gap'>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
checked={mobile}
|
||||
onChange={() => setMobile(!mobile)}
|
||||
name="checked"
|
||||
color="primary"
|
||||
/>
|
||||
<ButtonGroup aria-label="outlined primary button group">
|
||||
<Button onClick={() => setLayout("mobile")} variant={layout === "mobile" ? "contained" : "outlined"}>Mobile</Button>
|
||||
<Button onClick={() => setLayout("auto")} variant={layout === "auto" ? "contained" : "outlined"}>Auto</Button>
|
||||
<Button onClick={() => setLayout("desktop")} variant={layout === "desktop" ? "contained" : "outlined"}>Desktop</Button>
|
||||
</ButtonGroup>
|
||||
}
|
||||
label="Enable"
|
||||
labelPlacement="start"
|
||||
label=""
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='preferences-category tail-category'>
|
||||
<div className='preferences-item'>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
checked={!lockMobile}
|
||||
onChange={() => setLockMobile(!lockMobile)}
|
||||
name="checked"
|
||||
color="primary"
|
||||
/>
|
||||
<Switch
|
||||
checked={isSavePreferences}
|
||||
onChange={() => setIsSavePreferences(!isSavePreferences)}
|
||||
name="checked"
|
||||
color="primary"
|
||||
/>
|
||||
}
|
||||
label="Auto"
|
||||
labelPlacement="start"
|
||||
label="Save my settings (in the browser store)"
|
||||
labelPlacement="end"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -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? <ErasePopup handleClose={closeEraseMenu}/> : null}
|
||||
{uploadMenu? <UploadPopup handleClose={closeUploadMenu}/> : null}
|
||||
{info ? <InfoPopup info={gameInfo.data?.info} handleClose={closeInfo}/> : null}
|
||||
{preferencesPopup ? <PreferencesPopup mobile={mobile} setMobile={setMobile} lockMobile={lockMobile} setLockMobile={setLockMobile} handleClose={closePreferencesPopup}/> : null}
|
||||
{preferencesPopup ? <PreferencesPopup layout={layout} isSavePreferences={isSavePreferences} setLayout={setLayout} setIsSavePreferences={setIsSavePreferences} handleClose={closePreferencesPopup}/> : null}
|
||||
</>
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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<RootState> = 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,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -57,3 +57,12 @@ export function savePreferences(state: any) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
|
||||
export function removePreferences() {
|
||||
try {
|
||||
localStorage.removeItem(PREFERENCES_KEY);
|
||||
} catch (e) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user