From 7199e296b82cf68bca1d533455b26ca70f9c7523 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 8 May 2021 18:49:08 +0200 Subject: [PATCH] Added redux. Moved theme loading/setting to redux actions. Added automatic theme loading based on localStorage value. --- client/package-lock.json | 51 +++++++++++++++++++ client/package.json | 5 ++ client/src/App.module.css | 19 ------- client/src/App.tsx | 29 +++++++---- client/src/components/Home/Home.module.css | 19 +++++++ client/src/components/Home/Home.tsx | 29 +++++++++++ .../components/Themer/ThemePreview.module.css | 3 +- client/src/components/Themer/Themer.tsx | 24 ++++++--- client/src/store/actions/actionTypes.ts | 1 + client/src/store/actions/index.ts | 2 + client/src/store/actions/theme.ts | 24 +++++++++ client/src/store/reducers/index.ts | 9 ++++ client/src/store/reducers/theme.ts | 18 +++++++ client/src/store/store.ts | 9 ++++ 14 files changed, 203 insertions(+), 39 deletions(-) create mode 100644 client/src/components/Home/Home.module.css create mode 100644 client/src/components/Home/Home.tsx create mode 100644 client/src/store/actions/actionTypes.ts create mode 100644 client/src/store/actions/index.ts create mode 100644 client/src/store/actions/theme.ts create mode 100644 client/src/store/reducers/index.ts create mode 100644 client/src/store/reducers/theme.ts create mode 100644 client/src/store/store.ts diff --git a/client/package-lock.json b/client/package-lock.json index c8b05b0..6b4a88b 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -2290,6 +2290,15 @@ "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.8.tgz", "integrity": "sha512-S78QIYirQcUoo6UJZx9CSP0O2ix9IaeAXwQi26Rhr/+mg7qqPy8TzaxHSUut7eGjL8WmLccT7/MXf304WjqHcA==" }, + "@types/hoist-non-react-statics": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", + "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==", + "requires": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, "@types/html-minifier-terser": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz", @@ -2388,6 +2397,17 @@ "@types/react": "*" } }, + "@types/react-redux": { + "version": "7.1.16", + "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.16.tgz", + "integrity": "sha512-f/FKzIrZwZk7YEO9E1yoxIuDNRiDducxkFlkw/GNMGEnK9n4K8wJzlJBghpSuOVDgEUHoDkDF7Gi9lHNQR4siw==", + "requires": { + "@types/hoist-non-react-statics": "^3.3.0", + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0", + "redux": "^4.0.0" + } + }, "@types/react-router": { "version": "5.1.14", "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.14.tgz", @@ -12541,6 +12561,19 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "react-redux": { + "version": "7.2.4", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.4.tgz", + "integrity": "sha512-hOQ5eOSkEJEXdpIKbnRyl04LhaWabkDPV+Ix97wqQX3T3d2NQ8DUblNXXtNMavc7DpswyQM6xfaN4HQDKNY2JA==", + "requires": { + "@babel/runtime": "^7.12.1", + "@types/react-redux": "^7.1.16", + "hoist-non-react-statics": "^3.3.2", + "loose-envify": "^1.4.0", + "prop-types": "^15.7.2", + "react-is": "^16.13.1" + } + }, "react-refresh": { "version": "0.8.3", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.8.3.tgz", @@ -12773,6 +12806,24 @@ "strip-indent": "^3.0.0" } }, + "redux": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.1.0.tgz", + "integrity": "sha512-uI2dQN43zqLWCt6B/BMGRMY6db7TTY4qeHHfGeKb3EOhmOKjU3KdWvNLJyqaHRksv/ErdNH7cFZWg9jXtewy4g==", + "requires": { + "@babel/runtime": "^7.9.2" + } + }, + "redux-devtools-extension": { + "version": "2.13.9", + "resolved": "https://registry.npmjs.org/redux-devtools-extension/-/redux-devtools-extension-2.13.9.tgz", + "integrity": "sha512-cNJ8Q/EtjhQaZ71c8I9+BPySIBVEKssbPpskBfsXqb8HJ002A3KRVHfeRzwRo6mGPqsm7XuHTqNSNeS1Khig0A==" + }, + "redux-thunk": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.3.0.tgz", + "integrity": "sha512-km6dclyFnmcvxhAcrQV2AkZmPQjzPDjgVlQtR0EQjxZPyJ0BnMf3in1ryuR8A2qU0HldVRfxYXbFSKlI3N7Slw==" + }, "regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", diff --git a/client/package.json b/client/package.json index cb7757f..0ed0b77 100644 --- a/client/package.json +++ b/client/package.json @@ -12,11 +12,16 @@ "@types/node": "^12.20.12", "@types/react": "^17.0.5", "@types/react-dom": "^17.0.3", + "@types/react-redux": "^7.1.16", "@types/react-router-dom": "^5.1.7", "react": "^17.0.2", "react-dom": "^17.0.2", + "react-redux": "^7.2.4", "react-router-dom": "^5.2.0", "react-scripts": "4.0.3", + "redux": "^4.1.0", + "redux-devtools-extension": "^2.13.9", + "redux-thunk": "^2.3.0", "typescript": "^4.2.4", "web-vitals": "^1.1.2" }, diff --git a/client/src/App.module.css b/client/src/App.module.css index 6fd7c5f..e69de29 100644 --- a/client/src/App.module.css +++ b/client/src/App.module.css @@ -1,19 +0,0 @@ -.SettingsButton { - width: 35px; - height: 35px; - background-color: var(--color-accent); - border-radius: 50%; - position: absolute; - bottom: 10px; - left: 10px; - display: flex; - justify-content: center; - align-items: center; - opacity: 0.25; - transition: all 0.3s; -} - -.SettingsButton:hover { - cursor: pointer; - opacity: 1; -} \ No newline at end of file diff --git a/client/src/App.tsx b/client/src/App.tsx index bcd1d24..bede25d 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -1,23 +1,30 @@ -import { BrowserRouter, Route, Switch, Link } from 'react-router-dom'; +import { BrowserRouter, Route, Switch } from 'react-router-dom'; +import { setTheme } from './store/actions'; + +// Redux +import store from './store/store'; +import { Provider } from 'react-redux'; import classes from './App.module.css'; +import Home from './components/Home/Home'; import Apps from './components/Apps/Apps'; import Settings from './components/Settings/Settings'; -import Icon from './components/UI/Icon/Icon'; +if (localStorage.theme) { + store.dispatch(setTheme(localStorage.theme)); +} const App = (): JSX.Element => { return ( - - - - - - - - - + + + + + + + + ); } diff --git a/client/src/components/Home/Home.module.css b/client/src/components/Home/Home.module.css new file mode 100644 index 0000000..6fd7c5f --- /dev/null +++ b/client/src/components/Home/Home.module.css @@ -0,0 +1,19 @@ +.SettingsButton { + width: 35px; + height: 35px; + background-color: var(--color-accent); + border-radius: 50%; + position: absolute; + bottom: 10px; + left: 10px; + display: flex; + justify-content: center; + align-items: center; + opacity: 0.25; + transition: all 0.3s; +} + +.SettingsButton:hover { + cursor: pointer; + opacity: 1; +} \ No newline at end of file diff --git a/client/src/components/Home/Home.tsx b/client/src/components/Home/Home.tsx new file mode 100644 index 0000000..616f0a1 --- /dev/null +++ b/client/src/components/Home/Home.tsx @@ -0,0 +1,29 @@ +import { Link } from 'react-router-dom'; +import Icon from '../UI/Icon/Icon'; + +import classes from './Home.module.css'; +import { Container } from '../UI/Layout/Layout'; +import Headline from '../UI/Headline/Headline'; + +const Home = (): JSX.Element => { + const dateAndTime = (): string => { + const days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; + const months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; + + const date = new Date(); + + + return `${days[date.getDay()]}, ${date.getDate()} of ${months[date.getMonth()]} ${date.getFullYear()}`; + } + + return ( + + + + + + + ) +} + +export default Home; \ No newline at end of file diff --git a/client/src/components/Themer/ThemePreview.module.css b/client/src/components/Themer/ThemePreview.module.css index c9c1189..2c54f42 100644 --- a/client/src/components/Themer/ThemePreview.module.css +++ b/client/src/components/Themer/ThemePreview.module.css @@ -3,7 +3,7 @@ display: flex; flex-direction: column; justify-content: center; - align-items: center; + align-items: flex-start; } .ThemePreview:hover { @@ -13,6 +13,7 @@ .ThemePreview p { text-transform: capitalize; margin: 8px 0; + color: var(--color-primary); /* align-self: flex-start; */ } diff --git a/client/src/components/Themer/Themer.tsx b/client/src/components/Themer/Themer.tsx index 31cf847..ba9184e 100644 --- a/client/src/components/Themer/Themer.tsx +++ b/client/src/components/Themer/Themer.tsx @@ -1,4 +1,5 @@ -import { useEffect } from 'react'; +import { useEffect, Fragment } from 'react'; +import { connect } from 'react-redux'; import classes from './Themer.module.css'; @@ -8,7 +9,13 @@ import ThemePreview from './ThemePreview'; import { Container } from '../UI/Layout/Layout'; import Headline from '../UI/Headline/Headline'; -const Themer = (): JSX.Element => { +import { setTheme } from '../../store/actions'; + +interface ComponentProps { + setTheme: Function; +} + +const Themer = (props: ComponentProps): JSX.Element => { useEffect((): void => { if (localStorage.theme) { applyTheme(localStorage.theme); @@ -27,19 +34,20 @@ const Themer = (): JSX.Element => { } return ( - +
- + /> */}
- {themes.map((theme: Theme): JSX.Element => )} + {themes.map((theme: Theme): JSX.Element => )}
-
+ ) } -export default Themer; \ No newline at end of file + +export default connect(null, { setTheme })(Themer); \ No newline at end of file diff --git a/client/src/store/actions/actionTypes.ts b/client/src/store/actions/actionTypes.ts new file mode 100644 index 0000000..1446098 --- /dev/null +++ b/client/src/store/actions/actionTypes.ts @@ -0,0 +1 @@ +export const SET_THEME = 'SET_THEME'; diff --git a/client/src/store/actions/index.ts b/client/src/store/actions/index.ts new file mode 100644 index 0000000..158a6ed --- /dev/null +++ b/client/src/store/actions/index.ts @@ -0,0 +1,2 @@ +export * from './theme'; +export * from './actionTypes'; \ No newline at end of file diff --git a/client/src/store/actions/theme.ts b/client/src/store/actions/theme.ts new file mode 100644 index 0000000..70cf74d --- /dev/null +++ b/client/src/store/actions/theme.ts @@ -0,0 +1,24 @@ +import { Dispatch } from 'redux'; +import { themes } from '../../components/Themer/themes.json'; +import { Theme } from '../../interfaces/Theme'; +import { SET_THEME } from './actionTypes'; + +export const setTheme = (themeName: string) => (dispatch: Dispatch) => { + const theme = themes.find((theme: Theme) => theme.name === themeName); + + if (theme) { + localStorage.setItem('theme', themeName); + loadTheme(theme); + + dispatch({ + type: SET_THEME, + payload: themeName + }) + } +} + +export const loadTheme = (theme: Theme) => { + for (const [key, value] of Object.entries(theme.colors)) { + document.body.style.setProperty(`--color-${key}`, value); + } +} \ No newline at end of file diff --git a/client/src/store/reducers/index.ts b/client/src/store/reducers/index.ts new file mode 100644 index 0000000..e091dac --- /dev/null +++ b/client/src/store/reducers/index.ts @@ -0,0 +1,9 @@ +import { combineReducers } from 'redux'; + +import themeReducer from './theme'; + +const rootReducer = combineReducers({ + theme: themeReducer +}) + +export default rootReducer; \ No newline at end of file diff --git a/client/src/store/reducers/theme.ts b/client/src/store/reducers/theme.ts new file mode 100644 index 0000000..084168b --- /dev/null +++ b/client/src/store/reducers/theme.ts @@ -0,0 +1,18 @@ +import { SET_THEME } from '../actions/actionTypes'; + +const initialState = { + theme: 'blues' +} + +const themeReducer = (state = initialState, action: any) => { + switch (action.type) { + case SET_THEME: + return { + theme: action.payload + }; + default: + return state; + } +} + +export default themeReducer; \ No newline at end of file diff --git a/client/src/store/store.ts b/client/src/store/store.ts new file mode 100644 index 0000000..c0f4be4 --- /dev/null +++ b/client/src/store/store.ts @@ -0,0 +1,9 @@ +import { createStore, applyMiddleware } from 'redux'; +import { composeWithDevTools } from 'redux-devtools-extension'; +import thunk from 'redux-thunk'; +import rootReducer from './reducers'; +const initialState = {}; + +const store = createStore(rootReducer, initialState, composeWithDevTools(applyMiddleware(thunk))); + +export default store; \ No newline at end of file