add redux setup and typescript configuration
Change-Id: Ibe00e3e969d539d3898e412a0093ee2076bec857
diff --git a/client/package.json b/client/package.json
index b60b40e..3f70bca 100644
--- a/client/package.json
+++ b/client/package.json
@@ -4,42 +4,50 @@
"version": "0.1.0",
"private": true,
"dependencies": {
- "@babel/runtime": "^7.17.9",
- "@emotion/react": "^11.9.0",
- "@emotion/styled": "^11.8.1",
- "@mui/icons-material": "^5.6.2",
- "@mui/lab": "^5.0.0-alpha.79",
- "@mui/material": "^5.6.3",
- "@mui/styles": "^5.6.2",
- "@testing-library/jest-dom": "^4.2.4",
- "@testing-library/react": "^9.5.0",
- "@testing-library/user-event": "^7.2.1",
+ "@babel/runtime": "7.18.9",
+ "@emotion/react": "^11.10.0",
+ "@emotion/styled": "^11.10.0",
+ "@mui/icons-material": "^5.10.2",
+ "@mui/lab": "^5.0.0-alpha.96",
+ "@mui/material": "^5.10.2",
+ "@mui/styles": "^5.10.2",
+ "@reduxjs/toolkit": "^1.8.5",
+ "@testing-library/jest-dom": "^5.16.5",
+ "@testing-library/react": "^13.3.0",
+ "@testing-library/user-event": "^14.4.3",
"dayjs": "^1.11.5",
- "emoji-picker-react": "^3.5.1",
- "react": "^17.0.2",
- "react-dom": "^17.0.2",
+ "@types/jest": "^28.1.8",
+ "emoji-picker-react": "^3.6.1",
+ "framer-motion": "^7.2.1",
+ "react": "^18.2.0",
+ "react-dom": "^18.2.0",
"react-emoji-render": "^1.2.4",
"react-fetch-hook": "^1.9.5",
+ "react-redux": "^8.0.2",
"react-router-dom": "^6.3.0",
- "socket.io-client": "^4.5.0",
- "framer-motion": "^6.3.2"
+ "socket.io-client": "^4.5.1"
},
"devDependencies": {
- "@babel/core": "^7.17.9",
- "@babel/plugin-transform-runtime": "^7.17.0",
- "@babel/preset-env": "^7.16.11",
- "@babel/preset-react": "^7.16.7",
- "@pmmmwh/react-refresh-webpack-plugin": "^0.5.5",
- "babel-loader": "^8.2.2",
- "copy-webpack-plugin": "^8.1.1",
+ "@babel/core": "^7.18.13",
+ "@babel/plugin-transform-runtime": "^7.18.10",
+ "@babel/preset-env": "^7.18.10",
+ "@babel/preset-react": "^7.18.6",
+ "@pmmmwh/react-refresh-webpack-plugin": "^0.5.7",
+ "@types/node": "^18.7.13",
+ "@types/react": "^18.0.17",
+ "@types/react-dom": "^18.0.6",
+ "babel-loader": "^8.2.5",
+ "copy-webpack-plugin": "^11.0.0",
"css-loader": "^6.7.1",
"html-webpack-plugin": "^5.5.0",
- "react-refresh": "^0.12.0",
- "sass": "^1.51.0",
- "sass-loader": "^12.6.0",
+ "react-refresh": "^0.14.0",
+ "sass": "^1.54.5",
+ "sass-loader": "^13.0.2",
"style-loader": "^3.3.1",
- "webpack": "^5.72.0",
- "webpack-cli": "^4.9.2"
+ "ts-loader": "^9.3.1",
+ "typescript": "^4.7.4",
+ "webpack": "^5.74.0",
+ "webpack-cli": "^4.10.0"
},
"scripts": {
"build": "npx webpack"
diff --git a/client/redux/appSlice.ts b/client/redux/appSlice.ts
new file mode 100644
index 0000000..f0fc628
--- /dev/null
+++ b/client/redux/appSlice.ts
@@ -0,0 +1,37 @@
+import { createSlice, PayloadAction } from "@reduxjs/toolkit";
+import type { RootState } from "./store";
+
+// Define a type for the slice state
+interface appState {
+ value: number;
+}
+
+// Define the initial state using that type
+const initialState: appState = {
+ value: 0,
+};
+
+export const appSlice = createSlice({
+ name: "app",
+ // `createSlice` will infer the state type from the `initialState` argument
+ initialState,
+ reducers: {
+ increment: (state) => {
+ state.value += 1;
+ },
+ decrement: (state) => {
+ state.value -= 1;
+ },
+ // Use the PayloadAction type to declare the contents of `action.payload`
+ incrementByAmount: (state, action: PayloadAction<number>) => {
+ state.value += action.payload;
+ },
+ },
+});
+
+export const { increment, decrement, incrementByAmount } = appSlice.actions;
+
+// Other code such as selectors can use the imported `RootState` type
+export const selectCount = (state: RootState) => state.app.value;
+
+export default appSlice.reducer;
diff --git a/client/redux/hooks.ts b/client/redux/hooks.ts
new file mode 100644
index 0000000..6875ea7
--- /dev/null
+++ b/client/redux/hooks.ts
@@ -0,0 +1,6 @@
+import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux";
+import type { RootState, AppDispatch } from "./store";
+
+// Use throughout your app instead of plain `useDispatch` and `useSelector`
+export const useAppDispatch: () => AppDispatch = useDispatch;
+export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
diff --git a/client/redux/store.ts b/client/redux/store.ts
new file mode 100644
index 0000000..d28f3d0
--- /dev/null
+++ b/client/redux/store.ts
@@ -0,0 +1,13 @@
+import { configureStore } from "@reduxjs/toolkit";
+import appReducer from "./appSlice";
+
+export const store = configureStore({
+ reducer: {
+ app: appReducer,
+ },
+});
+
+// Infer the `RootState` and `AppDispatch` types from the store itself
+export type RootState = ReturnType<typeof store.getState>;
+// Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState}
+export type AppDispatch = typeof store.dispatch;
diff --git a/client/src/App.js b/client/src/App.js
index b685936..d0043df 100644
--- a/client/src/App.js
+++ b/client/src/App.js
@@ -19,6 +19,9 @@
import WelcomeAnimation from './components/welcome'
import defaultTheme from './themes/default'
+// import { useSelector, useDispatch } from 'react-redux'
+// import { useAppSelector, useAppDispatch } from '../redux/hooks'
+
const Home = (props) => {
console.log(`home ${props}`)
@@ -26,48 +29,65 @@
}
const App = (props) => {
- const [state, setState] = useState({
- loaded: false,
- auth: authManager.getState()
- })
- const [displayWelcome, setDisplayWelcome] = useState(true)
+ // const count = useSelector(state => state.counter.value)
+ // const dispatch = useDispatch();
+ // const count = useAppSelector((state) => state.counter.value);
+ // const dispatch = useAppDispatch();
- useEffect(() => {
- authManager.init(auth => {
- setState({ loaded: false, auth })
- })
- return () => authManager.deinit()
- }, []);
+ const [state, setState] = useState({
+ loaded: false,
+ auth: authManager.getState(),
+ });
+ const [displayWelcome, setDisplayWelcome] = useState(true);
- console.log("App render")
- if (displayWelcome) {
- return <WelcomeAnimation showSetup={!state.auth.setupComplete} onComplete={() => setDisplayWelcome(false)} />
- } else if (!state.auth.setupComplete) {
- return <Routes>
- <Route path="/setup" element={<ServerSetup />} />
- <Route path="/" element={<Navigate to="/setup" replace />} />
- <Route index path="*" element={<Navigate to="/setup" replace />} />
- </Routes>
- }
+ useEffect(() => {
+ authManager.init((auth) => {
+ setState({ loaded: false, auth });
+ });
+ return () => authManager.deinit();
+ }, []);
- return <ThemeProvider theme={defaultTheme}>
- <Routes>
- <Route path="/account">
- <Route index element={<AccountSelection />} />
- <Route path=":accountId">
- <Route index path="*" element={<JamiMessenger />} />
- <Route path="settings" element={<AccountSettings />} />
+ console.log("App render");
+
+ if (displayWelcome) {
+ return (
+ <WelcomeAnimation
+ showSetup={!state.auth.setupComplete}
+ onComplete={() => setDisplayWelcome(false)}
+ />
+ );
+ } else if (!state.auth.setupComplete) {
+ return (
+ <Routes>
+ <Route path="/setup" element={<ServerSetup />} />
+ <Route path="/" element={<Navigate to="/setup" replace />} />
+ <Route index path="*" element={<Navigate to="/setup" replace />} />
+ </Routes>
+ );
+ }
+
+ return (
+ <ThemeProvider theme={defaultTheme}>
+ <Routes>
+ <Route path="/account">
+ <Route index element={<AccountSelection />} />
+ <Route path=":accountId">
+ <Route index path="*" element={<JamiMessenger />} />
+ <Route path="settings" element={<AccountSettings />} />
+ </Route>
</Route>
- </Route>
- <Route path="/newAccount" element={<AccountCreationDialog />}>
- <Route path="jami" element={<JamiAccountDialog />} />
- </Route>
- <Route path="/setup" element={<ServerSetup />} />
- <Route path="/" index element={<Home />} />
- <Route path="*" element={<NotFoundPage />} />
- </Routes>
- {!state.auth.authenticated && <SignInPage key="signin" open={!state.auth.authenticated}/>}
+ <Route path="/newAccount" element={<AccountCreationDialog />}>
+ <Route path="jami" element={<JamiAccountDialog />} />
+ </Route>
+ <Route path="/setup" element={<ServerSetup />} />
+ <Route path="/" index element={<Home />} />
+ <Route path="*" element={<NotFoundPage />} />
+ </Routes>
+ {!state.auth.authenticated && (
+ <SignInPage key="signin" open={!state.auth.authenticated} />
+ )}
</ThemeProvider>
-}
+ );
+};
export default App
diff --git a/client/src/index.js b/client/src/index.js
index e607b8a..27b5591 100644
--- a/client/src/index.js
+++ b/client/src/index.js
@@ -1,28 +1,37 @@
-'use strict'
-import React from 'react'
-import ReactDOM from 'react-dom'
-import { BrowserRouter as Router } from 'react-router-dom'
-import App from './App.js'
-import './index.scss'
+"use strict";
+import React from "react";
+import ReactDOM from "react-dom";
+import { BrowserRouter as Router } from "react-router-dom";
+import App from "./App.js";
+import "./index.scss";
-const rootEl = document.getElementById('root')
+import { store } from "../redux/store";
+import { Provider } from "react-redux";
+//import { CssBaseline } from '@mui/material'
-const render = Component =>
-ReactDOM.render(
- <React.StrictMode>
- <Router>
- <Component />
- </Router>
- </React.StrictMode>,
- rootEl
-)
+//import * as serviceWorker from './serviceWorker'
+const rootEl = document.getElementById("root");
+var exports = {};
+
+const render = (Component) =>
+ ReactDOM.render(
+ <Provider store={store}>
+ <React.StrictMode>
+ <Router>
+ <Component />
+ </Router>
+ </React.StrictMode>
+ </Provider>,
+ rootEl
+ );
render(App)
-if (import.meta.webpackHot) import.meta.webpackHot.accept('./App', () => {
- try {
- render(App)
- } catch (e) {
- location.reload()
- }
-})
\ No newline at end of file
+if (import.meta.webpackHot)
+ import.meta.webpackHot.accept("./App", () => {
+ try {
+ render(App);
+ } catch (e) {
+ location.reload();
+ }
+ });
diff --git a/client/tsconfig.json b/client/tsconfig.json
new file mode 100644
index 0000000..c2fd010
--- /dev/null
+++ b/client/tsconfig.json
@@ -0,0 +1,103 @@
+{
+ "compilerOptions": {
+ /* Visit https://aka.ms/tsconfig to read more about this file */
+
+ /* Projects */
+ // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
+ // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
+ // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
+ // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
+ // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
+ // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
+
+ /* Language and Environment */
+ "target": "ES6", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
+ // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
+ // "jsx": "preserve", /* Specify what JSX code is generated. */
+ // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
+ // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
+ // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
+ // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
+ // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
+ // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
+ // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
+ // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
+ // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
+
+ /* Modules */
+ // "module": "commonjs", /* Specify what module code is generated. */
+ // "rootDir": "./", /* Specify the root folder within your source files. */
+ "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
+ // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
+ // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
+ // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
+ // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
+ // "types": [], /* Specify type package names to be included without being referenced in a source file. */
+ // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
+ // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
+ // "resolveJsonModule": true, /* Enable importing .json files. */
+ // "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
+
+ /* JavaScript Support */
+ "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
+ // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
+ // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
+
+ /* Emit */
+ // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
+ // "declarationMap": true, /* Create sourcemaps for d.ts files. */
+ // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
+ // "sourceMap": true, /* Create source map files for emitted JavaScript files. */
+ // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
+ // "outDir": "./", /* Specify an output folder for all emitted files. */
+ // "removeComments": true, /* Disable emitting comments. */
+ // "noEmit": true, /* Disable emitting files from a compilation. */
+ // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
+ // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
+ // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
+ // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
+ // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
+ // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
+ // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
+ // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
+ // "newLine": "crlf", /* Set the newline character for emitting files. */
+ // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
+ // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
+ // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
+ // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
+ // "declarationDir": "./", /* Specify the output directory for generated declaration files. */
+ // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
+
+ /* Interop Constraints */
+ // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
+ // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
+ "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
+ // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
+ "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
+
+ /* Type Checking */
+ // "strict": true, /* Enable all strict type-checking options. */
+ // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
+ // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
+ // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
+ // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
+ // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
+ // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
+ // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
+ // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
+ // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
+ // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
+ // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
+ // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
+ // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
+ // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
+ // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
+ // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
+ // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
+ // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
+
+ /* Completeness */
+ // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
+ "skipLibCheck": true /* Skip type checking all .d.ts files. */
+ }
+}
diff --git a/client/webpack.config.js b/client/webpack.config.js
index 022b823..7da8fb5 100644
--- a/client/webpack.config.js
+++ b/client/webpack.config.js
@@ -36,44 +36,62 @@
export default {
entry,
output: {
- path: resolve(__dirname, 'dist'),
- filename: 'bundle.js',
- publicPath: '/'
+ path: resolve(__dirname, "dist"),
+ filename: "bundle.js",
+ publicPath: "/",
},
devtool,
mode,
module: {
rules: [
{
- test: /\.jsx?/,
+ // test: /\.jsx?/,
+ test: /\.(js|jsx|ts|tsx)?$/,
resolve: {
- fullySpecified: false
+ fullySpecified: false,
},
exclude: /node_modules/,
use: {
- loader: 'babel-loader',
+ loader: "babel-loader",
options: {
plugins: babelLoaderPlugins,
presets: [
- ['@babel/preset-env', {
- useBuiltIns: 'entry',
- corejs: { version: '3.10', proposals: true },
- }],
- ['@babel/preset-react', {
- runtime: 'automatic'
- }]]
- }
- }
+ [
+ "@babel/preset-env",
+ {
+ useBuiltIns: "entry",
+ corejs: { version: "3.10", proposals: true },
+ },
+ ],
+ [
+ "@babel/preset-react",
+ {
+ runtime: "automatic",
+ },
+ ],
+ ],
+ },
+ },
},
{
test: /\.s[ac]ss$/i,
- use: ['style-loader', 'css-loader', 'sass-loader'],
+ use: ["style-loader", "css-loader", "sass-loader"],
},
{
test: /\.svg$/,
- use: ['@svgr/webpack']
- }
- ]
+ use: ["@svgr/webpack"],
+ },
+ {
+ // test: /\.tsx?$/,
+ test: /\.(js|jsx|ts|tsx)?$/,
+ exclude: /node_modules/,
+ loader: "ts-loader",
+ },
+ ],
},
- plugins
-}
\ No newline at end of file
+ resolve: {
+ modules: ["node_modules", resolve(__dirname)],
+ extensions: [".ts", ".tsx", ".js", ".jsx"],
+ },
+ plugins,
+};
\ No newline at end of file