From 0a9f640e5e8df02b650fb5539b69379bc1b516ac Mon Sep 17 00:00:00 2001 From: Claire Peng Date: Wed, 3 Sep 2025 19:49:48 +0100 Subject: [PATCH 01/14] reducers: update to ts --- client/{reducers.js => reducers.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename client/{reducers.js => reducers.ts} (100%) diff --git a/client/reducers.js b/client/reducers.ts similarity index 100% rename from client/reducers.js rename to client/reducers.ts From e85985dc5f330c4266ff15984d5547db383fb638 Mon Sep 17 00:00:00 2001 From: Claire Peng Date: Wed, 3 Sep 2025 19:51:53 +0100 Subject: [PATCH 02/14] reducers: update to named export --- client/reducers.ts | 4 +--- client/store.js | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/client/reducers.ts b/client/reducers.ts index f61d2585d6..f33babf41f 100644 --- a/client/reducers.ts +++ b/client/reducers.ts @@ -14,7 +14,7 @@ import sorting from './modules/IDE/reducers/sorting'; import loading from './modules/IDE/reducers/loading'; import collections from './modules/IDE/reducers/collections'; -const rootReducer = combineReducers({ +export const rootReducer = combineReducers({ ide, files, preferences, @@ -30,5 +30,3 @@ const rootReducer = combineReducers({ loading, collections }); - -export default rootReducer; diff --git a/client/store.js b/client/store.js index e74248f010..f43f2f141e 100644 --- a/client/store.js +++ b/client/store.js @@ -1,7 +1,7 @@ import { configureStore } from '@reduxjs/toolkit'; import listenerMiddleware from './middleware'; import DevTools from './modules/App/components/DevTools'; -import rootReducer from './reducers'; +import { rootReducer } from './reducers'; import { clearState, loadState } from './persistState'; import { getConfig } from './utils/getConfig'; From 391c672123aa2b886faf629ec33d2f063c26aa62 Mon Sep 17 00:00:00 2001 From: Claire Peng Date: Wed, 3 Sep 2025 19:56:24 +0100 Subject: [PATCH 03/14] constants: update to ts --- client/{constants.js => constants.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename client/{constants.js => constants.ts} (100%) diff --git a/client/constants.js b/client/constants.ts similarity index 100% rename from client/constants.js rename to client/constants.ts From 9a7031694cea701eee55c78edfe982a911688bd8 Mon Sep 17 00:00:00 2001 From: Claire Peng Date: Wed, 3 Sep 2025 20:28:15 +0100 Subject: [PATCH 04/14] modules/IDE/actions/preferences: update to ts, no-verify --- client/modules/IDE/actions/{preferences.js => preferences.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename client/modules/IDE/actions/{preferences.js => preferences.ts} (100%) diff --git a/client/modules/IDE/actions/preferences.js b/client/modules/IDE/actions/preferences.ts similarity index 100% rename from client/modules/IDE/actions/preferences.js rename to client/modules/IDE/actions/preferences.ts From 2a264d19bd82b1083c011389317789b581e07859 Mon Sep 17 00:00:00 2001 From: Claire Peng Date: Wed, 3 Sep 2025 20:43:27 +0100 Subject: [PATCH 05/14] modules/IDE/actions/preferences: wip update types, no-verify --- client/modules/IDE/actions/preferences.ts | 42 +++++++++++------------ 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/client/modules/IDE/actions/preferences.ts b/client/modules/IDE/actions/preferences.ts index f6e71504ee..bdcdbc8127 100644 --- a/client/modules/IDE/actions/preferences.ts +++ b/client/modules/IDE/actions/preferences.ts @@ -14,14 +14,14 @@ function updatePreferences(formParams, dispatch) { }); } -export function setPreferencesTab(value) { +export function setPreferencesTab(value: number) { return { type: ActionTypes.SET_PREFERENCES_TAB, value }; } -export function setFontSize(value) { +export function setFontSize(value: number) { return (dispatch, getState) => { // eslint-disable-line dispatch({ @@ -40,7 +40,7 @@ export function setFontSize(value) { }; } -export function setLineNumbers(value) { +export function setLineNumbers(value: boolean) { return (dispatch, getState) => { dispatch({ type: ActionTypes.SET_LINE_NUMBERS, @@ -58,7 +58,7 @@ export function setLineNumbers(value) { }; } -export function setAutocloseBracketsQuotes(value) { +export function setAutocloseBracketsQuotes(value: boolean) { return (dispatch, getState) => { dispatch({ type: ActionTypes.SET_AUTOCLOSE_BRACKETS_QUOTES, @@ -76,7 +76,7 @@ export function setAutocloseBracketsQuotes(value) { }; } -export function setAutocompleteHinter(value) { +export function setAutocompleteHinter(valu: boolean) { return (dispatch, getState) => { dispatch({ type: ActionTypes.SET_AUTOCOMPLETE_HINTER, @@ -94,7 +94,7 @@ export function setAutocompleteHinter(value) { }; } -export function setAutosave(value) { +export function setAutosave(value: boolean) { return (dispatch, getState) => { dispatch({ type: ActionTypes.SET_AUTOSAVE, @@ -112,7 +112,7 @@ export function setAutosave(value) { }; } -export function setLinewrap(value) { +export function setLinewrap(value: boolean) { return (dispatch, getState) => { dispatch({ type: ActionTypes.SET_LINEWRAP, @@ -130,7 +130,7 @@ export function setLinewrap(value) { }; } -export function setLintWarning(value) { +export function setLintWarning(value: boolean) { return (dispatch, getState) => { dispatch({ type: ActionTypes.SET_LINT_WARNING, @@ -148,7 +148,7 @@ export function setLintWarning(value) { }; } -export function setTextOutput(value) { +export function setTextOutput(value: boolean) { return (dispatch, getState) => { dispatch({ type: ActionTypes.SET_TEXT_OUTPUT, @@ -166,7 +166,7 @@ export function setTextOutput(value) { }; } -export function setGridOutput(value) { +export function setGridOutput(value: boolean) { return (dispatch, getState) => { dispatch({ type: ActionTypes.SET_GRID_OUTPUT, @@ -184,11 +184,13 @@ export function setGridOutput(value) { }; } -export function setTheme(value) { - // return { - // type: ActionTypes.SET_THEME, - // value - // }; +export enum AppThemes { + LIGHT = 'light', + DARK = 'dark', + CONTRAST = 'contrast' +} + +export function setTheme(value: AppThemes) { return (dispatch, getState) => { dispatch({ type: ActionTypes.SET_THEME, @@ -206,11 +208,7 @@ export function setTheme(value) { }; } -export function setAutorefresh(value) { - // return { - // type: ActionTypes.SET_AUTOREFRESH, - // value - // }; +export function setAutorefresh(value: boolean) { return (dispatch, getState) => { dispatch({ type: ActionTypes.SET_AUTOREFRESH, @@ -228,14 +226,14 @@ export function setAutorefresh(value) { }; } -export function setAllAccessibleOutput(value) { +export function setAllAccessibleOutput(value: boolean) { return (dispatch) => { dispatch(setTextOutput(value)); dispatch(setGridOutput(value)); }; } -export function setLanguage(value, { persistPreference = true } = {}) { +export function setLanguage(value: string, { persistPreference = true } = {}) { return (dispatch, getState) => { i18next.changeLanguage(value); dispatch({ From 728cfdbc8c9820f63bb785c52dc3387b70771598 Mon Sep 17 00:00:00 2001 From: Claire Peng Date: Wed, 3 Sep 2025 20:56:57 +0100 Subject: [PATCH 06/14] modules/IDE/actions/preferences: wip update types to sync with formparams types, no-verify --- client/modules/IDE/actions/preferences.ts | 78 +++++++++++++++++------ 1 file changed, 57 insertions(+), 21 deletions(-) diff --git a/client/modules/IDE/actions/preferences.ts b/client/modules/IDE/actions/preferences.ts index bdcdbc8127..2c0e71f177 100644 --- a/client/modules/IDE/actions/preferences.ts +++ b/client/modules/IDE/actions/preferences.ts @@ -2,7 +2,46 @@ import i18next from 'i18next'; import { apiClient } from '../../../utils/apiClient'; import * as ActionTypes from '../../../constants'; -function updatePreferences(formParams, dispatch) { +// Not included in Preferences Form to post to BE +type IdePreferencesTabIndex = number; +type IdeAllAccessibleOutput = boolean; + +// Included in Preferences Form to post to BE +type IdeFontSize = number; +type IdeLineNumber = boolean; +type IdeAutoCloseBracketQuotes = boolean; +type IdeAutoCompleteHinter = boolean; +type IdeAutoSave = boolean; +type IdeLineWrap = boolean; +type IdeLintWarning = boolean; +type IdeTextOutput = boolean; +type IdeGridOutput = boolean; +enum IdeTheme { + LIGHT = 'light', + DARK = 'dark', + CONTRAST = 'contrast' +} +type IdeAutoRefresh = boolean; +type IdeLanguage = string; + +export interface PreferencesFormParam { + preferences: Partial<{ + fontSize: IdeFontSize; + lineNumbers: IdeLineNumber; + autocloseBracketsQuotes: IdeAutoCloseBracketQuotes; + autocompleteHinter: IdeAutoCompleteHinter; + autosave: IdeAutoSave; + linewrap: IdeLineWrap; + lintWarning: IdeLintWarning; + textOutput: IdeTextOutput; + gridOutput: IdeGridOutput; + theme: IdeTheme; + autorefresh: IdeAutoRefresh; + language: IdeLanguage; + }>; +} + +function updatePreferences(formParams: PreferencesFormParam, dispatch) { apiClient .put('/preferences', formParams) .then(() => {}) @@ -14,14 +53,14 @@ function updatePreferences(formParams, dispatch) { }); } -export function setPreferencesTab(value: number) { +export function setPreferencesTab(value: IdePreferencesTabIndex) { return { type: ActionTypes.SET_PREFERENCES_TAB, value }; } -export function setFontSize(value: number) { +export function setFontSize(value: IdeFontSize) { return (dispatch, getState) => { // eslint-disable-line dispatch({ @@ -40,7 +79,7 @@ export function setFontSize(value: number) { }; } -export function setLineNumbers(value: boolean) { +export function setLineNumbers(value: IdeLineNumber) { return (dispatch, getState) => { dispatch({ type: ActionTypes.SET_LINE_NUMBERS, @@ -58,7 +97,7 @@ export function setLineNumbers(value: boolean) { }; } -export function setAutocloseBracketsQuotes(value: boolean) { +export function setAutocloseBracketsQuotes(value: IdeAutoCloseBracketQuotes) { return (dispatch, getState) => { dispatch({ type: ActionTypes.SET_AUTOCLOSE_BRACKETS_QUOTES, @@ -76,7 +115,7 @@ export function setAutocloseBracketsQuotes(value: boolean) { }; } -export function setAutocompleteHinter(valu: boolean) { +export function setAutocompleteHinter(value: IdeAutoCompleteHinter) { return (dispatch, getState) => { dispatch({ type: ActionTypes.SET_AUTOCOMPLETE_HINTER, @@ -94,7 +133,7 @@ export function setAutocompleteHinter(valu: boolean) { }; } -export function setAutosave(value: boolean) { +export function setAutosave(value: IdeAutoSave) { return (dispatch, getState) => { dispatch({ type: ActionTypes.SET_AUTOSAVE, @@ -112,7 +151,7 @@ export function setAutosave(value: boolean) { }; } -export function setLinewrap(value: boolean) { +export function setLinewrap(value: IdeLineWrap) { return (dispatch, getState) => { dispatch({ type: ActionTypes.SET_LINEWRAP, @@ -130,7 +169,7 @@ export function setLinewrap(value: boolean) { }; } -export function setLintWarning(value: boolean) { +export function setLintWarning(value: IdeLintWarning) { return (dispatch, getState) => { dispatch({ type: ActionTypes.SET_LINT_WARNING, @@ -148,7 +187,7 @@ export function setLintWarning(value: boolean) { }; } -export function setTextOutput(value: boolean) { +export function setTextOutput(value: IdeTextOutput) { return (dispatch, getState) => { dispatch({ type: ActionTypes.SET_TEXT_OUTPUT, @@ -166,7 +205,7 @@ export function setTextOutput(value: boolean) { }; } -export function setGridOutput(value: boolean) { +export function setGridOutput(value: IdeGridOutput) { return (dispatch, getState) => { dispatch({ type: ActionTypes.SET_GRID_OUTPUT, @@ -184,13 +223,7 @@ export function setGridOutput(value: boolean) { }; } -export enum AppThemes { - LIGHT = 'light', - DARK = 'dark', - CONTRAST = 'contrast' -} - -export function setTheme(value: AppThemes) { +export function setTheme(value: IdeTheme) { return (dispatch, getState) => { dispatch({ type: ActionTypes.SET_THEME, @@ -208,7 +241,7 @@ export function setTheme(value: AppThemes) { }; } -export function setAutorefresh(value: boolean) { +export function setAutorefresh(value: IdeAutoRefresh) { return (dispatch, getState) => { dispatch({ type: ActionTypes.SET_AUTOREFRESH, @@ -226,14 +259,17 @@ export function setAutorefresh(value: boolean) { }; } -export function setAllAccessibleOutput(value: boolean) { +export function setAllAccessibleOutput(value: IdeAllAccessibleOutput) { return (dispatch) => { dispatch(setTextOutput(value)); dispatch(setGridOutput(value)); }; } -export function setLanguage(value: string, { persistPreference = true } = {}) { +export function setLanguage( + value: IdeLanguage, + { persistPreference = true } = {} +) { return (dispatch, getState) => { i18next.changeLanguage(value); dispatch({ From e1f584b858804527b83ad13ce481ccd1fd49dadd Mon Sep 17 00:00:00 2001 From: Claire Peng Date: Wed, 3 Sep 2025 21:59:23 +0100 Subject: [PATCH 07/14] client/store.js: update to ts, no-verify --- client/{store.js => store.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename client/{store.js => store.ts} (100%) diff --git a/client/store.js b/client/store.ts similarity index 100% rename from client/store.js rename to client/store.ts From 1e01b5a05209f088ce929892bb91ed085de6b771 Mon Sep 17 00:00:00 2001 From: Claire Peng Date: Wed, 3 Sep 2025 22:08:48 +0100 Subject: [PATCH 08/14] client/store.js: update to named export, no-verify --- .storybook/preview.js | 4 ++-- client/index.integration.test.jsx | 2 +- client/index.jsx | 2 +- client/store.ts | 6 +++--- client/test-utils.js | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.storybook/preview.js b/.storybook/preview.js index 9260b91c98..080b022fe5 100644 --- a/.storybook/preview.js +++ b/.storybook/preview.js @@ -2,9 +2,9 @@ import React from 'react'; import { Provider } from 'react-redux'; import { MemoryRouter } from 'react-router'; -import configureStore from '../client/store'; +import { configureStore } from '../client/store'; import '../client/i18n-test'; -import '../client/styles/storybook.css' +import '../client/styles/storybook.css'; import { withThemeProvider, themeToolbarItem } from './decorator-theme'; const initialState = window.__INITIAL_STATE__; diff --git a/client/index.integration.test.jsx b/client/index.integration.test.jsx index 66819b1536..68ecd6efc7 100644 --- a/client/index.integration.test.jsx +++ b/client/index.integration.test.jsx @@ -4,7 +4,7 @@ import React from 'react'; import Routing from './routes'; import { reduxRender, act, waitFor, screen, within } from './test-utils'; -import configureStore from './store'; +import { configureStore } from './store'; import * as Actions from './modules/User/actions'; import { userResponse } from './testData/testServerResponses'; diff --git a/client/index.jsx b/client/index.jsx index f050230c7b..8732ab99cb 100644 --- a/client/index.jsx +++ b/client/index.jsx @@ -4,7 +4,7 @@ import { Provider } from 'react-redux'; import { Router } from 'react-router-dom'; import browserHistory from './browserHistory'; -import configureStore from './store'; +import { configureStore } from './store'; import Routing from './routes'; import ThemeProvider from './modules/App/components/ThemeProvider'; import Loader from './modules/App/components/loader'; diff --git a/client/store.ts b/client/store.ts index f43f2f141e..57a9eaf716 100644 --- a/client/store.ts +++ b/client/store.ts @@ -1,4 +1,4 @@ -import { configureStore } from '@reduxjs/toolkit'; +import { configureStore as _configureStore } from '@reduxjs/toolkit'; import listenerMiddleware from './middleware'; import DevTools from './modules/App/components/DevTools'; import { rootReducer } from './reducers'; @@ -15,11 +15,11 @@ export function showReduxDevTools() { ); } -export default function setupStore(initialState) { +export function configureStore(initialState) { const savedState = loadState(); clearState(); - const store = configureStore({ + const store = _configureStore({ reducer: rootReducer, middleware: (getDefaultMiddleware) => getDefaultMiddleware({ diff --git a/client/test-utils.js b/client/test-utils.js index 9b7c8aab31..0d48a4eae9 100644 --- a/client/test-utils.js +++ b/client/test-utils.js @@ -23,7 +23,7 @@ import { Context as ResponsiveContext } from 'react-responsive'; import i18n from './i18n-test'; import ThemeProvider from './modules/App/components/ThemeProvider'; -import configureStore from './store'; +import { configureStore } from './store'; import theme, { Theme } from './theme'; export const history = createMemoryHistory(); From 8b70d25a1380bb3f3442a5d72f5a6ef5fc30248c Mon Sep 17 00:00:00 2001 From: Claire Peng Date: Wed, 3 Sep 2025 22:10:03 +0100 Subject: [PATCH 09/14] client/middleware.js: updaet to ts, no-verify --- client/{middleware.js => middleware.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename client/{middleware.js => middleware.ts} (100%) diff --git a/client/middleware.js b/client/middleware.ts similarity index 100% rename from client/middleware.js rename to client/middleware.ts From 3e4c67711949aca0f77a09b8f5b5a196023585e4 Mon Sep 17 00:00:00 2001 From: Claire Peng Date: Wed, 3 Sep 2025 22:10:47 +0100 Subject: [PATCH 10/14] client/middleware.js: updaet to named export, no-verify --- client/middleware.ts | 4 +--- client/store.ts | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/client/middleware.ts b/client/middleware.ts index 0b4230ec9d..385600bc15 100644 --- a/client/middleware.ts +++ b/client/middleware.ts @@ -1,5 +1,3 @@ import { createListenerMiddleware } from '@reduxjs/toolkit'; -const listenerMiddleware = createListenerMiddleware(); - -export default listenerMiddleware; +export const listenerMiddleware = createListenerMiddleware(); diff --git a/client/store.ts b/client/store.ts index 57a9eaf716..40d2c5666a 100644 --- a/client/store.ts +++ b/client/store.ts @@ -1,5 +1,5 @@ import { configureStore as _configureStore } from '@reduxjs/toolkit'; -import listenerMiddleware from './middleware'; +import { listenerMiddleware } from './middleware'; import DevTools from './modules/App/components/DevTools'; import { rootReducer } from './reducers'; import { clearState, loadState } from './persistState'; From 3c6fb6ae37e93cfc0b3ad6bf4233829cebc99a75 Mon Sep 17 00:00:00 2001 From: Claire Peng Date: Wed, 3 Sep 2025 22:12:13 +0100 Subject: [PATCH 11/14] client/persistState.js: update to ts, no-verify --- client/{persistState.js => persistState.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename client/{persistState.js => persistState.ts} (100%) diff --git a/client/persistState.js b/client/persistState.ts similarity index 100% rename from client/persistState.js rename to client/persistState.ts From 6e43dcda742006284ff691cfc2195febcc885c66 Mon Sep 17 00:00:00 2001 From: Claire Peng Date: Wed, 3 Sep 2025 22:15:22 +0100 Subject: [PATCH 12/14] persistState: add types --- client/persistState.ts | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/client/persistState.ts b/client/persistState.ts index 457dba016b..3f8771ecb7 100644 --- a/client/persistState.ts +++ b/client/persistState.ts @@ -1,11 +1,12 @@ -/* - Saves and loads a snapshot of the Redux store - state to session storage -*/ +/** + * Saves and loads a snapshot of the Redux store + * state to session storage + */ const key = 'p5js-editor'; -const storage = sessionStorage; +const storage: Storage = sessionStorage; -export const saveState = (state) => { +// Use a generic type for state so consumers can specify the shape +export const saveState = (state: T): void => { try { storage.setItem(key, JSON.stringify(state)); } catch (error) { @@ -13,15 +14,18 @@ export const saveState = (state) => { } }; -export const loadState = () => { +// Returns the stored state or null if not found +export const loadState = (): T | null => { try { - return JSON.parse(storage.getItem(key)); + const item = storage.getItem(key); + if (!item) return null; + return JSON.parse(item) as T; } catch (error) { console.warn('Failed to retrieve initialize state from storage:', error); return null; } }; -export const clearState = () => { +export const clearState = (): void => { storage.removeItem(key); }; From ba3fc2ab71504a17d83d5f6735bba63ad217a75c Mon Sep 17 00:00:00 2001 From: Claire Peng Date: Thu, 4 Sep 2025 16:42:07 +0100 Subject: [PATCH 13/14] store: add types --- client/store.ts | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/client/store.ts b/client/store.ts index 40d2c5666a..7bc8039e78 100644 --- a/client/store.ts +++ b/client/store.ts @@ -1,22 +1,35 @@ -import { configureStore as _configureStore } from '@reduxjs/toolkit'; +import { + configureStore as _configureStore, + PreloadedState +} from '@reduxjs/toolkit'; import { listenerMiddleware } from './middleware'; import DevTools from './modules/App/components/DevTools'; import { rootReducer } from './reducers'; import { clearState, loadState } from './persistState'; import { getConfig } from './utils/getConfig'; +// necessary to add redux devtool extension to Window interface +declare global { + interface Window { + __REDUX_DEVTOOLS_EXTENSION__?: any; + } +} + // Enable DevTools only when rendering on client and during development. // Display the dock monitor only if no browser extension is found. export function showReduxDevTools() { return ( getConfig('CLIENT') && getConfig('NODE_ENV') === 'development' && + // eslint-disable-next-line no-underscore-dangle !window.__REDUX_DEVTOOLS_EXTENSION__ ); } -export function configureStore(initialState) { - const savedState = loadState(); +export function configureStore( + initialState: PreloadedState +) { + const savedState = loadState(); clearState(); const store = _configureStore({ @@ -32,9 +45,9 @@ export function configureStore(initialState) { enhancers: showReduxDevTools() ? [DevTools.instrument()] : [] }); - if (module.hot) { + if ((module as any).hot) { // Enable Webpack hot module replacement for reducers - module.hot.accept('./reducers', () => { + (module as any).hot.accept('./reducers', () => { const nextRootReducer = require('./reducers').default; // eslint-disable-line global-require store.replaceReducer(nextRootReducer); }); From b1653d0c7e96a96c9dbf0f8207e85c80776921cf Mon Sep 17 00:00:00 2001 From: Claire Peng Date: Thu, 4 Sep 2025 16:45:20 +0100 Subject: [PATCH 14/14] modules/IDE/reducers/preferences: update to ts, no-verify --- client/modules/IDE/reducers/{preferences.js => preferences.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename client/modules/IDE/reducers/{preferences.js => preferences.ts} (100%) diff --git a/client/modules/IDE/reducers/preferences.js b/client/modules/IDE/reducers/preferences.ts similarity index 100% rename from client/modules/IDE/reducers/preferences.js rename to client/modules/IDE/reducers/preferences.ts