Skip to content

Commit 5bca4a1

Browse files
authored
Refactor: Settings (#80)
1 parent 4b6efab commit 5bca4a1

File tree

16 files changed

+425
-307
lines changed

16 files changed

+425
-307
lines changed

Projects/MathlibDemo/lake-manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"type": "git",
66
"subDir": null,
77
"scope": "leanprover-community",
8-
"rev": "a36c84ab8236a4869899268a42a44af07daa21ed",
8+
"rev": "d68c4dc09f5e000d3c968adae8def120a0758729",
99
"name": "mathlib",
1010
"manifestFile": "lake-manifest.json",
1111
"inputRev": "master",

client/src/App.tsx

Lines changed: 36 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,8 @@ import * as path from 'path'
1010

1111
// Local imports
1212
import LeanLogo from './assets/logo.svg'
13-
import defaultSettings, { IPreferencesContext, lightThemes, preferenceParams } from './config/settings'
13+
import { lightThemes } from './settings/settings-types'
1414
import { Menu } from './Navigation'
15-
import { PreferencesContext } from './Popups/Settings'
1615
import { Entries } from './utils/Entries'
1716
import { save } from './utils/SaveToFile'
1817
import { fixedEncodeURIComponent, formatArgs, lookupUrl, parseArgs } from './utils/UrlParsing'
@@ -21,6 +20,9 @@ import { useWindowDimensions } from './utils/WindowWidth'
2120
// CSS
2221
import './css/App.css'
2322
import './css/Editor.css'
23+
import { mobileAtom, settingsAtom } from './settings/settings-atoms'
24+
import { useAtom } from 'jotai/react'
25+
import { screenWidthAtom } from './store/window-atoms'
2426

2527
/** Returns true if the browser wants dark mode */
2628
function isBrowserDefaultDark() {
@@ -33,8 +35,9 @@ function App() {
3335
const [dragging, setDragging] = useState<boolean | null>(false)
3436
const [editor, setEditor] = useState<monaco.editor.IStandaloneCodeEditor>()
3537
const [leanMonaco, setLeanMonaco] = useState<LeanMonaco>()
36-
const [loaded, setLoaded] = useState<boolean>(false)
37-
const [preferences, setPreferences] = useState<IPreferencesContext>(defaultSettings)
38+
const [settings] = useAtom(settingsAtom)
39+
const [mobile] = useAtom(mobileAtom)
40+
const [, setScreenWidth] = useAtom(screenWidthAtom)
3841
const { width } = useWindowDimensions()
3942

4043
// Lean4monaco options
@@ -83,67 +86,13 @@ function App() {
8386
setProject(project)
8487
}, [])
8588

86-
// Load preferences from store in the beginning
89+
// save the screen width in jotai
8790
useEffect(() => {
88-
// only load them once
89-
if (loaded) { return }
90-
console.debug('[Lean4web] Loading preferences')
91-
92-
let saveInLocalStore = false;
93-
let newPreferences: { [K in keyof IPreferencesContext]: IPreferencesContext[K] } = { ...preferences }
94-
for (const [key, value] of (Object.entries(preferences) as Entries<IPreferencesContext>)) {
95-
// prefer URL params over stored
96-
const searchParams = new URLSearchParams(window.location.search);
97-
let storedValue = (
98-
preferenceParams.includes(key) && // only for keys we explictly check for
99-
searchParams.has(key) && searchParams.get(key))
100-
?? window.localStorage.getItem(key)
101-
if (storedValue) {
102-
saveInLocalStore = window.localStorage.getItem(key) === storedValue
103-
console.debug(`[Lean4web] Found value for ${key}: ${storedValue}`)
104-
if (typeof value === 'string') {
105-
if (key == 'theme') {
106-
const theme = storedValue.toLowerCase().includes('dark') ? "Visual Studio Dark" : "Visual Studio Light"
107-
newPreferences[key] = theme
108-
}
109-
else {
110-
newPreferences[key] = storedValue
111-
}
112-
} else if (typeof value === 'boolean') {
113-
newPreferences[key] = (storedValue === "true")
114-
} else {
115-
// other values aren't implemented yet.
116-
console.error(`[Lean4web] Preferences (key: ${key}) contain a value of unsupported type: ${typeof value}`)
117-
}
118-
} else {
119-
// no stored preferences, set a default value
120-
if (key == 'theme') {
121-
if (isBrowserDefaultDark()) {
122-
console.debug("[Lean4web] Preferences: Set dark theme.")
123-
newPreferences['theme'] = 'Visual Studio Dark'
124-
} else {
125-
console.debug("[Lean4web] Preferences: Set light theme.")
126-
newPreferences['theme'] = 'Visual Studio Light'
127-
}
128-
}
129-
}
130-
}
131-
newPreferences['saveInLocalStore'] = saveInLocalStore
132-
setPreferences(newPreferences)
133-
setLoaded(true)
134-
}, [])
135-
136-
// Use the window width to switch between mobile/desktop layout
137-
useEffect(() => {
138-
// Wait for preferences to be loaded
139-
if (!loaded) { return }
91+
const handleResize = () => setScreenWidth(window.innerWidth)
92+
window.addEventListener('resize', handleResize)
93+
return () => window.removeEventListener('resize', handleResize)
94+
}, [setScreenWidth])
14095

141-
const _mobile = width < 800
142-
const searchParams = new URLSearchParams(window.location.search);
143-
if (!(searchParams.has("mobile") || preferences.saveInLocalStore) && _mobile !== preferences.mobile) {
144-
setPreferences({ ...preferences, mobile: _mobile })
145-
}
146-
}, [width, loaded])
14796

14897
// Update LeanMonaco options when preferences are loaded or change
14998
useEffect(() => {
@@ -162,37 +111,36 @@ function App() {
162111
* for the desired setting, select "Copy Setting as JSON" from the "More Actions"
163112
* menu next to the selected setting, and paste the copied string here.
164113
*/
165-
"workbench.colorTheme": preferences.theme,
114+
"workbench.colorTheme": settings.theme,
166115
"editor.tabSize": 2,
167116
// "editor.rulers": [100],
168117
"editor.lightbulb.enabled": "on",
169-
"editor.wordWrap": preferences.wordWrap ? "on" : "off",
118+
"editor.wordWrap": settings.wordWrap ? "on" : "off",
170119
"editor.wrappingStrategy": "advanced",
171120
"editor.semanticHighlighting.enabled": true,
172-
"editor.acceptSuggestionOnEnter": preferences.acceptSuggestionOnEnter ? "on" : "off",
121+
"editor.acceptSuggestionOnEnter": settings.acceptSuggestionOnEnter ? "on" : "off",
173122
"lean4.input.eagerReplacementEnabled": true,
174-
"lean4.infoview.showGoalNames": preferences.showGoalNames,
123+
"lean4.infoview.showGoalNames": settings.showGoalNames,
175124
"lean4.infoview.emphasizeFirstGoal": true,
176125
"lean4.infoview.showExpectedType": false,
177126
"lean4.infoview.showTooltipOnHover": false,
178-
"lean4.input.leader": preferences.abbreviationCharacter
127+
"lean4.input.leader": settings.abbreviationCharacter
179128
}
180129
}
181130
setOptions(_options)
182-
}, [editorRef, project, preferences])
131+
}, [editorRef, project, settings])
183132

184133
// Setting up the editor and infoview
185134
useEffect(() => {
186-
// Wait for preferences to be loaded
187-
if (!loaded) { return }
135+
// if (project === undefined) return
188136
console.debug('[Lean4web] Restarting editor')
189137
var _leanMonaco = new LeanMonaco()
190138
var leanMonacoEditor = new LeanMonacoEditor()
191139

192140
_leanMonaco.setInfoviewElement(infoviewRef.current!)
193141
;(async () => {
194142
await _leanMonaco.start(options)
195-
await leanMonacoEditor.start(editorRef.current!, path.join(project, `${project}.lean`), code)
143+
await leanMonacoEditor.start(editorRef.current!, path.join(project, `${project}.lean`), code ?? '')
196144

197145
setEditor(leanMonacoEditor.editor)
198146
setLeanMonaco(_leanMonaco)
@@ -201,7 +149,7 @@ function App() {
201149
// Monaco does not support clipboard pasting as all browsers block it
202150
// due to security reasons. Therefore we use a codeMirror editor overlay
203151
// which features good mobile support (but no Lean support yet)
204-
if (preferences.mobile) {
152+
if (mobile) {
205153
leanMonacoEditor.editor?.addAction({
206154
id: "myPaste",
207155
label: "Paste: open 'Plain Editor' for editing on mobile",
@@ -274,7 +222,7 @@ function App() {
274222
leanMonacoEditor.dispose()
275223
_leanMonaco.dispose()
276224
}
277-
}, [loaded, project, preferences, options, infoviewRef, editorRef])
225+
}, [project, settings, options, infoviewRef, editorRef])
278226

279227
// Load content from source URL.
280228
// Once the editor is loaded, this reads the content of any provided `url=` in the URL and
@@ -330,7 +278,7 @@ function App() {
330278
code: null,
331279
codez: null
332280
}
333-
} else if (preferences.compress) {
281+
} else if (settings.compress) {
334282
// LZ padds the string with trailing `=`, which mess up the argument parsing
335283
// and aren't needed for LZ encoding, so we remove them.
336284
const compressed = LZString.compressToBase64(code).replace(/=*$/, '')
@@ -402,7 +350,7 @@ function App() {
402350
}
403351
}, [handleKeyDown, handleKeyUp])
404352

405-
return <PreferencesContext.Provider value={{preferences, setPreferences}}>
353+
return (
406354
<div className="app monaco-editor">
407355
<nav>
408356
<LeanLogo />
@@ -426,34 +374,34 @@ function App() {
426374
}}
427375
gutterStyle={(_dimension, gutterSize, _index) => {
428376
return {
429-
'width': preferences.mobile ? '100%' : `${gutterSize}px`,
430-
'height': preferences.mobile ? `${gutterSize}px` : '100%',
431-
'cursor': preferences.mobile ? 'row-resize' : 'col-resize',
432-
'margin-left': preferences.mobile ? 0 : `-${gutterSize}px`,
433-
'margin-top': preferences.mobile ? `-${gutterSize}px` : 0,
377+
'width': mobile ? '100%' : `${gutterSize}px`,
378+
'height': mobile ? `${gutterSize}px` : '100%',
379+
'cursor': mobile ? 'row-resize' : 'col-resize',
380+
'margin-left': mobile ? 0 : `-${gutterSize}px`,
381+
'margin-top': mobile ? `-${gutterSize}px` : 0,
434382
'z-index': 0,
435383
}}}
436384
gutterSize={5}
437385
onDragStart={() => setDragging(true)} onDragEnd={() => setDragging(false)}
438-
sizes={preferences.mobile ? [50, 50] : [70, 30]}
439-
direction={preferences.mobile ? "vertical" : "horizontal"}
440-
style={{flexDirection: preferences.mobile ? "column" : "row"}}>
386+
sizes={mobile ? [50, 50] : [70, 30]}
387+
direction={mobile ? "vertical" : "horizontal"}
388+
style={{flexDirection: mobile ? "column" : "row"}}>
441389
<div className='codeview-wrapper'
442-
style={preferences.mobile ? {width : '100%'} : {height: '100%'}} >
390+
style={mobile ? {width : '100%'} : {height: '100%'}} >
443391
{ codeMirror &&
444392
<CodeMirror
445393
className="codeview plain"
446394
value={code}
447395
extensions={[EditorView.lineWrapping]}
448396
height='100%'
449397
maxHeight='100%'
450-
theme={lightThemes.includes(preferences.theme) ? 'light' : 'dark'}
398+
theme={lightThemes.includes(settings.theme) ? 'light' : 'dark'}
451399
onChange={setContent} />
452400
}
453401
<div ref={editorRef} className={`codeview${codeMirror ? ' hidden' : ''}`} />
454402
</div>
455403
<div ref={infoviewRef} className="vscode-light infoview"
456-
style={preferences.mobile ? {width : '100%'} : {height: '100%'}} >
404+
style={mobile ? {width : '100%'} : {height: '100%'}} >
457405
<p className={`editor-support-warning${codeMirror ? '' : ' hidden'}`} >
458406
You are in the plain text editor<br /><br />
459407
Go back to the Monaco Editor (click <FontAwesomeIcon icon={faCode}/>)
@@ -462,8 +410,7 @@ function App() {
462410
</div>
463411
</Split>
464412
</div>
465-
</PreferencesContext.Provider>
466-
413+
)
467414
}
468415

469416
export default App

client/src/Navigation.tsx

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { IconDefinition, faArrowRotateRight, faCode, faInfoCircle } from '@forta
44
import ZulipIcon from './assets/zulip.svg'
55
import { faArrowUpRightFromSquare, faDownload, faBars, faXmark, faShield, faHammer, faGear, faStar, faUpload, faCloudArrowUp } from '@fortawesome/free-solid-svg-icons'
66

7-
import SettingsPopup, { PreferencesContext } from './Popups/Settings'
87
import PrivacyPopup from './Popups/PrivacyPolicy'
98
import ImpressumPopup from './Popups/Impressum'
109
import ToolsPopup from './Popups/Tools'
@@ -16,6 +15,9 @@ import './css/Modal.css'
1615
import './css/Navigation.css'
1716
import { save } from './utils/SaveToFile'
1817
import { lookupUrl } from './utils/UrlParsing'
18+
import { mobileAtom, settingsAtom } from './settings/settings-atoms'
19+
import { useAtom } from 'jotai'
20+
import SettingsPopup from './settings/SettingsPopup'
1921

2022
/** A button to appear in the hamburger menu or to navigation bar. */
2123
export const NavButton: FC<{
@@ -154,7 +156,7 @@ export const Menu: FC <{
154156
const [toolsOpen, setToolsOpen] = useState(false)
155157
const [settingsOpen, setSettingsOpen] = useState(false)
156158

157-
const { preferences } = useContext(PreferencesContext)
159+
const [mobile] = useAtom(mobileAtom)
158160

159161
const loadFromUrl = (url: string, project:string|undefined=undefined) => {
160162
url = lookupUrl(url)
@@ -184,10 +186,10 @@ export const Menu: FC <{
184186
<option key={proj.folder} value={proj.folder}>{proj.name ?? proj.folder}</option>
185187
)}
186188
</select>
187-
{ preferences.mobile &&
189+
{ mobile &&
188190
<NavButton icon={faCode} text={codeMirror ? "Lean" : "Text"} onClick={() => {setCodeMirror(!codeMirror)}}/>
189191
}
190-
{ !preferences.mobile &&
192+
{ !mobile &&
191193
<FlexibleMenu isInDropdown={false}
192194
setOpenNav={setOpenNav}
193195
openExample={openExample}
@@ -200,7 +202,7 @@ export const Menu: FC <{
200202
setLoadZulipOpen={setLoadZulipOpen} />
201203
}
202204
<Dropdown open={openNav} setOpen={setOpenNav} icon={openNav ? faXmark : faBars} onClick={() => {setOpenExample(false); setOpenLoad(false)}}>
203-
{ preferences.mobile &&
205+
{ mobile &&
204206
<FlexibleMenu isInDropdown={true}
205207
setOpenNav={setOpenNav}
206208
openExample={openExample}

client/src/Popups/LoadUrl.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ const LoadUrlPopup: FC<{
2828
<h2>Load from URL</h2>
2929
{error ? <p className="form-error">{error}</p>: null}
3030
<form onSubmit={handleLoad}>
31-
<input autoFocus type="text" placeholder="Paste URL here" ref={urlRef}/>
31+
<input name="import URL" autoFocus type="text" placeholder="Paste URL here" ref={urlRef}/>
3232
<input type="submit" value="Load"/>
3333
</form>
3434
</Popup>

client/src/Popups/LoadZulip.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ const LoadZulipPopup: FC<{
4242
</p>
4343
{error ? <p className="form-error">{error}</p>: null}
4444
<form onSubmit={handleLoad}>
45-
<textarea autoFocus placeholder="Paste Zulip message" ref={textInputRef}/>
45+
<textarea name="Zulip message input" autoFocus placeholder="Paste Zulip message" ref={textInputRef}/>
4646
<input type="submit" value="Parse message"/>
4747
</form>
4848
</Popup>

0 commit comments

Comments
 (0)