From eaab31aacc2fb021b0c344eb2aa7db608ab834e4 Mon Sep 17 00:00:00 2001 From: pmjklemm Date: Tue, 4 Jan 2022 21:11:13 +0100 Subject: [PATCH 01/33] fix sameTab for prefix==l --- client/src/utility/searchParser.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/client/src/utility/searchParser.ts b/client/src/utility/searchParser.ts index 82ce28c..fac888a 100644 --- a/client/src/utility/searchParser.ts +++ b/client/src/utility/searchParser.ts @@ -41,9 +41,8 @@ export const searchParser = (searchQuery: string): SearchResult => { if (prefix === 'l') { result.isLocal = true; - } else { - result.sameTab = config.searchSameTab; } + result.sameTab = config.searchSameTab; return result; } From 04e80b339cfa7617568dadc409c3834f52b47548 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Malak?= Date: Fri, 4 Feb 2022 13:09:47 +0100 Subject: [PATCH 02/33] Changed order and names of some setting tabs --- .../CustomQueries/CustomQueries.module.css | 0 .../CustomQueries/CustomQueries.tsx | 0 .../CustomQueries/QueriesForm.tsx | 0 .../GeneralSettings.tsx} | 12 +++++++----- client/src/components/Settings/Settings.tsx | 6 +++--- client/src/components/Settings/settings.json | 14 +++++++------- client/src/interfaces/Forms.ts | 2 +- client/src/types/ConfigFormData.ts | 4 ++-- .../utility/templateObjects/settingsTemplate.ts | 4 ++-- 9 files changed, 22 insertions(+), 20 deletions(-) rename client/src/components/Settings/{SearchSettings => GeneralSettings}/CustomQueries/CustomQueries.module.css (100%) rename client/src/components/Settings/{SearchSettings => GeneralSettings}/CustomQueries/CustomQueries.tsx (100%) rename client/src/components/Settings/{SearchSettings => GeneralSettings}/CustomQueries/QueriesForm.tsx (100%) rename client/src/components/Settings/{SearchSettings/SearchSettings.tsx => GeneralSettings/GeneralSettings.tsx} (92%) diff --git a/client/src/components/Settings/SearchSettings/CustomQueries/CustomQueries.module.css b/client/src/components/Settings/GeneralSettings/CustomQueries/CustomQueries.module.css similarity index 100% rename from client/src/components/Settings/SearchSettings/CustomQueries/CustomQueries.module.css rename to client/src/components/Settings/GeneralSettings/CustomQueries/CustomQueries.module.css diff --git a/client/src/components/Settings/SearchSettings/CustomQueries/CustomQueries.tsx b/client/src/components/Settings/GeneralSettings/CustomQueries/CustomQueries.tsx similarity index 100% rename from client/src/components/Settings/SearchSettings/CustomQueries/CustomQueries.tsx rename to client/src/components/Settings/GeneralSettings/CustomQueries/CustomQueries.tsx diff --git a/client/src/components/Settings/SearchSettings/CustomQueries/QueriesForm.tsx b/client/src/components/Settings/GeneralSettings/CustomQueries/QueriesForm.tsx similarity index 100% rename from client/src/components/Settings/SearchSettings/CustomQueries/QueriesForm.tsx rename to client/src/components/Settings/GeneralSettings/CustomQueries/QueriesForm.tsx diff --git a/client/src/components/Settings/SearchSettings/SearchSettings.tsx b/client/src/components/Settings/GeneralSettings/GeneralSettings.tsx similarity index 92% rename from client/src/components/Settings/SearchSettings/SearchSettings.tsx rename to client/src/components/Settings/GeneralSettings/GeneralSettings.tsx index 9b057f5..8c2e5fd 100644 --- a/client/src/components/Settings/SearchSettings/SearchSettings.tsx +++ b/client/src/components/Settings/GeneralSettings/GeneralSettings.tsx @@ -3,7 +3,7 @@ import { useState, useEffect, FormEvent, ChangeEvent, Fragment } from 'react'; import { useDispatch, useSelector } from 'react-redux'; // Typescript -import { Query, SearchForm } from '../../../interfaces'; +import { Query, GeneralForm } from '../../../interfaces'; // Components import { CustomQueries } from './CustomQueries/CustomQueries'; @@ -12,7 +12,7 @@ import { CustomQueries } from './CustomQueries/CustomQueries'; import { Button, SettingsHeadline, InputGroup } from '../../UI'; // Utils -import { inputHandler, searchSettingsTemplate } from '../../../utility'; +import { inputHandler, generalSettingsTemplate } from '../../../utility'; // Data import { queries } from '../../../utility/searchQueries.json'; @@ -22,7 +22,7 @@ import { State } from '../../../store/reducers'; import { bindActionCreators } from 'redux'; import { actionCreators } from '../../../store'; -export const SearchSettings = (): JSX.Element => { +export const GeneralSettings = (): JSX.Element => { const { loading, customQueries, config } = useSelector( (state: State) => state.config ); @@ -31,7 +31,9 @@ export const SearchSettings = (): JSX.Element => { const { updateConfig } = bindActionCreators(actionCreators, dispatch); // Initial state - const [formData, setFormData] = useState(searchSettingsTemplate); + const [formData, setFormData] = useState( + generalSettingsTemplate + ); // Get config useEffect(() => { @@ -53,7 +55,7 @@ export const SearchSettings = (): JSX.Element => { e: ChangeEvent, options?: { isNumber?: boolean; isBool?: boolean } ) => { - inputHandler({ + inputHandler({ e, options, setStateHandler: setFormData, diff --git a/client/src/components/Settings/Settings.tsx b/client/src/components/Settings/Settings.tsx index f9f5102..7a297c9 100644 --- a/client/src/components/Settings/Settings.tsx +++ b/client/src/components/Settings/Settings.tsx @@ -16,7 +16,7 @@ import { WeatherSettings } from './WeatherSettings/WeatherSettings'; import { UISettings } from './UISettings/UISettings'; import { AppDetails } from './AppDetails/AppDetails'; import { StyleSettings } from './StyleSettings/StyleSettings'; -import { SearchSettings } from './SearchSettings/SearchSettings'; +import { GeneralSettings } from './GeneralSettings/GeneralSettings'; import { DockerSettings } from './DockerSettings/DockerSettings'; import { ProtectedRoute } from '../Routing/ProtectedRoute'; @@ -59,8 +59,8 @@ export const Settings = (): JSX.Element => { component={WeatherSettings} /> Date: Fri, 4 Feb 2022 14:59:48 +0100 Subject: [PATCH 03/33] Moved some settings between general and ui tabs --- .../Settings/UISettings/UISettings.tsx | 143 +++++------------- client/src/interfaces/Forms.ts | 18 +-- client/src/types/ConfigFormData.ts | 4 +- .../templateObjects/settingsTemplate.ts | 18 +-- 4 files changed, 60 insertions(+), 123 deletions(-) diff --git a/client/src/components/Settings/UISettings/UISettings.tsx b/client/src/components/Settings/UISettings/UISettings.tsx index 075a427..1e75154 100644 --- a/client/src/components/Settings/UISettings/UISettings.tsx +++ b/client/src/components/Settings/UISettings/UISettings.tsx @@ -7,28 +7,22 @@ import { bindActionCreators } from 'redux'; import { actionCreators } from '../../../store'; // Typescript -import { OtherSettingsForm } from '../../../interfaces'; +import { UISettingsForm } from '../../../interfaces'; // UI import { InputGroup, Button, SettingsHeadline } from '../../UI'; // Utils -import { otherSettingsTemplate, inputHandler } from '../../../utility'; +import { uiSettingsTemplate, inputHandler } from '../../../utility'; export const UISettings = (): JSX.Element => { - const { - config: { loading, config }, - bookmarks: { categories }, - } = useSelector((state: State) => state); + const { loading, config } = useSelector((state: State) => state.config); const dispatch = useDispatch(); - const { updateConfig, sortApps, sortCategories, sortBookmarks } = - bindActionCreators(actionCreators, dispatch); + const { updateConfig } = bindActionCreators(actionCreators, dispatch); // Initial state - const [formData, setFormData] = useState( - otherSettingsTemplate - ); + const [formData, setFormData] = useState(uiSettingsTemplate); // Get config useEffect(() => { @@ -46,16 +40,6 @@ export const UISettings = (): JSX.Element => { // Update local page title document.title = formData.customTitle; - - // Sort entities with new settings - if (formData.useOrdering !== config.useOrdering) { - sortApps(); - sortCategories(); - - for (let { id } of categories) { - sortBookmarks(id); - } - } }; // Input handler @@ -63,7 +47,7 @@ export const UISettings = (): JSX.Element => { e: ChangeEvent, options?: { isNumber?: boolean; isBool?: boolean } ) => { - inputHandler({ + inputHandler({ e, options, setStateHandler: setFormData, @@ -88,6 +72,36 @@ export const UISettings = (): JSX.Element => { /> + {/* === SEARCH OPTIONS === */} + + {/* HIDE SEARCHBAR */} + + + + + + {/* AUTOFOCUS SEARCHBAR */} + + + + + {/* === HEADER OPTIONS === */} {/* HIDE HEADER */} @@ -160,8 +174,8 @@ export const UISettings = (): JSX.Element => { onChange={(e) => inputChangeHandler(e)} /> - Greetings must be separated with semicolon. Only 4 messages can be - used + Greetings must be separated with semicolon. All 4 messages must be + filled, even if they are the same @@ -193,85 +207,8 @@ export const UISettings = (): JSX.Element => { Names must be separated with semicolon - {/* === BEAHVIOR OPTIONS === */} - - {/* PIN APPS */} - - - - - - {/* PIN CATEGORIES */} - - - - - - {/* SORT TYPE */} - - - - - - {/* APPS OPPENING */} - - - - - - {/* BOOKMARKS OPPENING */} - - - - - - {/* === MODULES OPTIONS === */} - + {/* === SECTIONS OPTIONS === */} + {/* HIDE APPS */} diff --git a/client/src/interfaces/Forms.ts b/client/src/interfaces/Forms.ts index a3f999e..11e2739 100644 --- a/client/src/interfaces/Forms.ts +++ b/client/src/interfaces/Forms.ts @@ -9,28 +9,28 @@ export interface WeatherForm { } export interface GeneralForm { - hideSearch: boolean; defaultSearchProvider: string; searchSameTab: boolean; - disableAutofocus: boolean; -} - -export interface OtherSettingsForm { - customTitle: string; pinAppsByDefault: boolean; pinCategoriesByDefault: boolean; - hideHeader: boolean; - hideApps: boolean; - hideCategories: boolean; useOrdering: string; appsSameTab: boolean; bookmarksSameTab: boolean; +} + +export interface UISettingsForm { + customTitle: string; + hideHeader: boolean; + hideApps: boolean; + hideCategories: boolean; useAmericanDate: boolean; greetingsSchema: string; daySchema: string; monthSchema: string; showTime: boolean; hideDate: boolean; + hideSearch: boolean; + disableAutofocus: boolean; } export interface DockerSettingsForm { diff --git a/client/src/types/ConfigFormData.ts b/client/src/types/ConfigFormData.ts index f22d65a..5f1c3e5 100644 --- a/client/src/types/ConfigFormData.ts +++ b/client/src/types/ConfigFormData.ts @@ -1,6 +1,6 @@ import { DockerSettingsForm, - OtherSettingsForm, + UISettingsForm, GeneralForm, ThemeSettingsForm, WeatherForm, @@ -10,5 +10,5 @@ export type ConfigFormData = | WeatherForm | GeneralForm | DockerSettingsForm - | OtherSettingsForm + | UISettingsForm | ThemeSettingsForm; diff --git a/client/src/utility/templateObjects/settingsTemplate.ts b/client/src/utility/templateObjects/settingsTemplate.ts index 6ab937f..af3d137 100644 --- a/client/src/utility/templateObjects/settingsTemplate.ts +++ b/client/src/utility/templateObjects/settingsTemplate.ts @@ -1,21 +1,16 @@ import { DockerSettingsForm, - OtherSettingsForm, + UISettingsForm, GeneralForm, ThemeSettingsForm, WeatherForm, } from '../../interfaces'; -export const otherSettingsTemplate: OtherSettingsForm = { +export const uiSettingsTemplate: UISettingsForm = { customTitle: document.title, - pinAppsByDefault: true, - pinCategoriesByDefault: true, hideHeader: false, hideApps: false, hideCategories: false, - useOrdering: 'createdAt', - appsSameTab: false, - bookmarksSameTab: false, useAmericanDate: false, greetingsSchema: 'Good evening!;Good afternoon!;Good morning!;Good night!', daySchema: 'Sunday;Monday;Tuesday;Wednesday;Thursday;Friday;Saturday', @@ -23,6 +18,8 @@ export const otherSettingsTemplate: OtherSettingsForm = { 'January;February;March;April;May;June;July;August;September;October;November;December', showTime: false, hideDate: false, + hideSearch: false, + disableAutofocus: false, }; export const weatherSettingsTemplate: WeatherForm = { @@ -34,10 +31,13 @@ export const weatherSettingsTemplate: WeatherForm = { }; export const generalSettingsTemplate: GeneralForm = { - hideSearch: false, searchSameTab: false, defaultSearchProvider: 'l', - disableAutofocus: false, + pinAppsByDefault: true, + pinCategoriesByDefault: true, + useOrdering: 'createdAt', + appsSameTab: false, + bookmarksSameTab: false, }; export const dockerSettingsTemplate: DockerSettingsForm = { From 12295a6f68b8776aa187c63f4e43dff95e9a2dd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Malak?= Date: Fri, 4 Feb 2022 15:01:40 +0100 Subject: [PATCH 04/33] Moved some settings between general and ui tabs - continued. Fixed settings links in apps and categories tables --- .../src/components/Apps/AppTable/AppTable.tsx | 2 +- .../Bookmarks/Table/CategoryTable.tsx | 2 +- .../GeneralSettings/GeneralSettings.tsx | 131 +++++++++++++----- 3 files changed, 102 insertions(+), 33 deletions(-) diff --git a/client/src/components/Apps/AppTable/AppTable.tsx b/client/src/components/Apps/AppTable/AppTable.tsx index 003e1a7..8279c77 100644 --- a/client/src/components/Apps/AppTable/AppTable.tsx +++ b/client/src/components/Apps/AppTable/AppTable.tsx @@ -94,7 +94,7 @@ export const AppTable = (props: Props): JSX.Element => { ) : (

Custom order is disabled. You can change it in the{' '} - settings + settings

)} diff --git a/client/src/components/Bookmarks/Table/CategoryTable.tsx b/client/src/components/Bookmarks/Table/CategoryTable.tsx index d909cab..5a88097 100644 --- a/client/src/components/Bookmarks/Table/CategoryTable.tsx +++ b/client/src/components/Bookmarks/Table/CategoryTable.tsx @@ -102,7 +102,7 @@ export const CategoryTable = ({ openFormForUpdating }: Props): JSX.Element => { ) : (

Custom order is disabled. You can change it in the{' '} - settings + settings

)} diff --git a/client/src/components/Settings/GeneralSettings/GeneralSettings.tsx b/client/src/components/Settings/GeneralSettings/GeneralSettings.tsx index 8c2e5fd..4173c72 100644 --- a/client/src/components/Settings/GeneralSettings/GeneralSettings.tsx +++ b/client/src/components/Settings/GeneralSettings/GeneralSettings.tsx @@ -23,12 +23,14 @@ import { bindActionCreators } from 'redux'; import { actionCreators } from '../../../store'; export const GeneralSettings = (): JSX.Element => { - const { loading, customQueries, config } = useSelector( - (state: State) => state.config - ); + const { + config: { loading, customQueries, config }, + bookmarks: { categories }, + } = useSelector((state: State) => state); const dispatch = useDispatch(); - const { updateConfig } = bindActionCreators(actionCreators, dispatch); + const { updateConfig, sortApps, sortCategories, sortBookmarks } = + bindActionCreators(actionCreators, dispatch); // Initial state const [formData, setFormData] = useState( @@ -48,6 +50,16 @@ export const GeneralSettings = (): JSX.Element => { // Save settings await updateConfig(formData); + + // Sort entities with new settings + if (formData.useOrdering !== config.useOrdering) { + sortApps(); + sortCategories(); + + for (let { id } of categories) { + sortBookmarks(id); + } + } }; // Input handler @@ -65,12 +77,95 @@ export const GeneralSettings = (): JSX.Element => { return ( - {/* GENERAL SETTINGS */}
formSubmitHandler(e)} style={{ marginBottom: '30px' }} > + {/* === GENERAL OPTIONS === */} + {/* SORT TYPE */} + + + + + + {/* === APPS OPTIONS === */} + + {/* PIN APPS */} + + + + + + {/* APPS OPPENING */} + + + + + + {/* === BOOKMARKS OPTIONS === */} + + {/* PIN CATEGORIES */} + + + + + + {/* BOOKMARKS OPPENING */} + + + + + + {/* SEARCH SETTINGS */} + - - - - - - - - - - From 2d5cce9fdb583f2bf4b6a27b8ded7e5351cad31f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Malak?= Date: Mon, 14 Feb 2022 13:22:41 +0100 Subject: [PATCH 05/33] Fixed bug with app description not updating. Fixed bug with local search prefix not working --- CHANGELOG.md | 5 +++++ client/src/components/Apps/AppForm/AppForm.tsx | 2 ++ client/src/components/SearchBar/SearchBar.tsx | 3 +-- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9502adf..fe33b19 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +### v2.2.2 (2022-02-TBA) +- Fixed bug with local search not working when using prefix ([#289](https://github.com/pawelmalak/flame/issues/289)) +- Fixed bug with app description not updating when using custom icon ([#310](https://github.com/pawelmalak/flame/issues/310)) +- Changed some of the settings tabs + ### v2.2.1 (2022-01-08) - Local search will now include app descriptions ([#266](https://github.com/pawelmalak/flame/issues/266)) - Fixed bug with unsupported characters in local search [#279](https://github.com/pawelmalak/flame/issues/279)) diff --git a/client/src/components/Apps/AppForm/AppForm.tsx b/client/src/components/Apps/AppForm/AppForm.tsx index c2dc07a..a751ad6 100644 --- a/client/src/components/Apps/AppForm/AppForm.tsx +++ b/client/src/components/Apps/AppForm/AppForm.tsx @@ -64,7 +64,9 @@ export const AppForm = ({ modalHandler }: Props): JSX.Element => { if (customIcon) { data.append('icon', customIcon); } + data.append('name', formData.name); + data.append('description', formData.description); data.append('url', formData.url); data.append('isPublic', `${formData.isPublic ? 1 : 0}`); diff --git a/client/src/components/SearchBar/SearchBar.tsx b/client/src/components/SearchBar/SearchBar.tsx index dd0efe4..9920073 100644 --- a/client/src/components/SearchBar/SearchBar.tsx +++ b/client/src/components/SearchBar/SearchBar.tsx @@ -69,8 +69,7 @@ export const SearchBar = (props: Props): JSX.Element => { ); if (isLocal) { - // no additional encoding required for local search - setLocalSearch(inputRef.current.value); + setLocalSearch(search); } if (e.code === 'Enter' || e.code === 'NumpadEnter') { From 76dc3c44c8bd617bf8db9c4a17abce3b4a74e824 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Malak?= Date: Mon, 14 Feb 2022 13:58:57 +0100 Subject: [PATCH 06/33] Added option to get user location directly from the app --- CHANGELOG.md | 1 + .../WeatherSettings/WeatherSettings.tsx | 24 ++++++++++++------- .../UI/Forms/InputGroup/InputGroup.module.css | 4 ++-- 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fe33b19..c28eefd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ ### v2.2.2 (2022-02-TBA) +- Added option to get user location directly from the app ([#287](https://github.com/pawelmalak/flame/issues/287)) - Fixed bug with local search not working when using prefix ([#289](https://github.com/pawelmalak/flame/issues/289)) - Fixed bug with app description not updating when using custom icon ([#310](https://github.com/pawelmalak/flame/issues/310)) - Changed some of the settings tabs diff --git a/client/src/components/Settings/WeatherSettings/WeatherSettings.tsx b/client/src/components/Settings/WeatherSettings/WeatherSettings.tsx index 19ba7d4..a6819d8 100644 --- a/client/src/components/Settings/WeatherSettings/WeatherSettings.tsx +++ b/client/src/components/Settings/WeatherSettings/WeatherSettings.tsx @@ -82,6 +82,19 @@ export const WeatherSettings = (): JSX.Element => { }); }; + // Get user location + const getLocation = () => { + window.navigator.geolocation.getCurrentPosition( + ({ coords: { latitude, longitude } }) => { + setFormData({ + ...formData, + lat: latitude, + long: longitude, + }); + } + ); + }; + return (
formSubmitHandler(e)}> @@ -120,15 +133,8 @@ export const WeatherSettings = (): JSX.Element => { step="any" lang="en-150" /> - - You can use - - {' '} - latlong.net - + + Click to get current location diff --git a/client/src/components/UI/Forms/InputGroup/InputGroup.module.css b/client/src/components/UI/Forms/InputGroup/InputGroup.module.css index 93b74f1..3d67554 100644 --- a/client/src/components/UI/Forms/InputGroup/InputGroup.module.css +++ b/client/src/components/UI/Forms/InputGroup/InputGroup.module.css @@ -23,7 +23,7 @@ .InputGroup span { font-size: 12px; - color: var(--color-primary) + color: var(--color-primary); } .InputGroup span a { @@ -37,4 +37,4 @@ .InputGroup textarea { resize: none; height: 50vh; -} \ No newline at end of file +} From 1098a04fb9c1912fcfecbd554d0f314679d0308e Mon Sep 17 00:00:00 2001 From: Lukas Frischknecht Date: Thu, 17 Feb 2022 19:27:40 +0100 Subject: [PATCH 07/33] fix: Update Permissions on Startup --- .docker/Dockerfile | 2 +- .docker/Dockerfile.multiarch | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.docker/Dockerfile b/.docker/Dockerfile index 701547e..457a387 100644 --- a/.docker/Dockerfile +++ b/.docker/Dockerfile @@ -27,4 +27,4 @@ EXPOSE 5005 ENV NODE_ENV=production ENV PASSWORD=flame_password -CMD ["node", "server.js"] +CMD ["sh", "-c", "chown -R node /app/data && node server.js"] \ No newline at end of file diff --git a/.docker/Dockerfile.multiarch b/.docker/Dockerfile.multiarch index 42f5082..1c4fb20 100644 --- a/.docker/Dockerfile.multiarch +++ b/.docker/Dockerfile.multiarch @@ -28,4 +28,4 @@ EXPOSE 5005 ENV NODE_ENV=production ENV PASSWORD=flame_password -CMD ["node", "server.js"] \ No newline at end of file +CMD ["sh", "-c", "chown -R node /app/data && node server.js"] \ No newline at end of file From c2e81832a9a88b47f5a847e926f3c456858135db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Malak?= Date: Mon, 21 Mar 2022 12:16:48 +0100 Subject: [PATCH 08/33] Added build scripts. Pushed version 2.2.2 --- .dev/build_latest.sh | 2 ++ .dev/build_multiarch.sh | 6 ++++++ .env | 2 +- .gitignore | 3 +-- CHANGELOG.md | 3 ++- client/.env | 2 +- 6 files changed, 13 insertions(+), 5 deletions(-) create mode 100644 .dev/build_latest.sh create mode 100644 .dev/build_multiarch.sh diff --git a/.dev/build_latest.sh b/.dev/build_latest.sh new file mode 100644 index 0000000..9621433 --- /dev/null +++ b/.dev/build_latest.sh @@ -0,0 +1,2 @@ +docker build -t pawelmalak/flame -t "pawelmalak/flame:$1" -f .docker/Dockerfile "$2" \ + && docker push pawelmalak/flame && docker push "pawelmalak/flame:$1" \ No newline at end of file diff --git a/.dev/build_multiarch.sh b/.dev/build_multiarch.sh new file mode 100644 index 0000000..ef1e10c --- /dev/null +++ b/.dev/build_multiarch.sh @@ -0,0 +1,6 @@ +docker buildx build \ + --platform linux/arm/v7,linux/arm64,linux/amd64 \ + -f .docker/Dockerfile.multiarch \ + -t pawelmalak/flame:multiarch \ + -t "pawelmalak/flame:multiarch$1" \ + --push "$2" \ No newline at end of file diff --git a/.env b/.env index d5f54d5..887f01e 100644 --- a/.env +++ b/.env @@ -1,5 +1,5 @@ PORT=5005 NODE_ENV=development -VERSION=2.2.1 +VERSION=2.2.2 PASSWORD=flame_password SECRET=e02eb43d69953658c6d07311d6313f2d4467672cb881f96b29368ba1f3f4da4b \ No newline at end of file diff --git a/.gitignore b/.gitignore index 147804b..d53b3ef 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ node_modules data public -!client/public -build.sh \ No newline at end of file +!client/public \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index c28eefd..e3d3e1d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,8 @@ -### v2.2.2 (2022-02-TBA) +### v2.2.2 (2022-03-21) - Added option to get user location directly from the app ([#287](https://github.com/pawelmalak/flame/issues/287)) - Fixed bug with local search not working when using prefix ([#289](https://github.com/pawelmalak/flame/issues/289)) - Fixed bug with app description not updating when using custom icon ([#310](https://github.com/pawelmalak/flame/issues/310)) +- Changed permissions to some files and directories created by Flame - Changed some of the settings tabs ### v2.2.1 (2022-01-08) diff --git a/client/.env b/client/.env index edd69d9..f31a910 100644 --- a/client/.env +++ b/client/.env @@ -1 +1 @@ -REACT_APP_VERSION=2.2.1 \ No newline at end of file +REACT_APP_VERSION=2.2.2 \ No newline at end of file From ee0b4354933e5565138b6c28c6d9bbcf09b392b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Malak?= Date: Wed, 23 Mar 2022 13:02:32 +0100 Subject: [PATCH 09/33] Separated theme components --- .../GeneralSettings/GeneralSettings.tsx | 2 +- .../Themer/ThemeBuilder/ThemeBuilder.tsx | 3 ++ .../ThemeGrid.module.css} | 2 +- .../Settings/Themer/ThemeGrid/ThemeGrid.tsx | 30 +++++++++++++++++++ .../ThemePreview.module.css | 0 .../{ => ThemePreview}/ThemePreview.tsx | 2 +- .../src/components/Settings/Themer/Themer.tsx | 22 +++++--------- 7 files changed, 44 insertions(+), 17 deletions(-) create mode 100644 client/src/components/Settings/Themer/ThemeBuilder/ThemeBuilder.tsx rename client/src/components/Settings/Themer/{Themer.module.css => ThemeGrid/ThemeGrid.module.css} (99%) create mode 100644 client/src/components/Settings/Themer/ThemeGrid/ThemeGrid.tsx rename client/src/components/Settings/Themer/{ => ThemePreview}/ThemePreview.module.css (100%) rename client/src/components/Settings/Themer/{ => ThemePreview}/ThemePreview.tsx (93%) diff --git a/client/src/components/Settings/GeneralSettings/GeneralSettings.tsx b/client/src/components/Settings/GeneralSettings/GeneralSettings.tsx index 4173c72..d88208e 100644 --- a/client/src/components/Settings/GeneralSettings/GeneralSettings.tsx +++ b/client/src/components/Settings/GeneralSettings/GeneralSettings.tsx @@ -164,7 +164,7 @@ export const GeneralSettings = (): JSX.Element => { - {/* SEARCH SETTINGS */} + {/* === SEARCH OPTIONS === */} diff --git a/client/src/components/Settings/Themer/ThemeBuilder/ThemeBuilder.tsx b/client/src/components/Settings/Themer/ThemeBuilder/ThemeBuilder.tsx new file mode 100644 index 0000000..c95eb38 --- /dev/null +++ b/client/src/components/Settings/Themer/ThemeBuilder/ThemeBuilder.tsx @@ -0,0 +1,3 @@ +export const ThemeBuilder = (): JSX.Element => { + return

theme builder

; +}; diff --git a/client/src/components/Settings/Themer/Themer.module.css b/client/src/components/Settings/Themer/ThemeGrid/ThemeGrid.module.css similarity index 99% rename from client/src/components/Settings/Themer/Themer.module.css rename to client/src/components/Settings/Themer/ThemeGrid/ThemeGrid.module.css index 986f6c5..65dbd44 100644 --- a/client/src/components/Settings/Themer/Themer.module.css +++ b/client/src/components/Settings/Themer/ThemeGrid/ThemeGrid.module.css @@ -15,4 +15,4 @@ .ThemerGrid { grid-template-columns: 1fr 1fr 1fr; } -} \ No newline at end of file +} diff --git a/client/src/components/Settings/Themer/ThemeGrid/ThemeGrid.tsx b/client/src/components/Settings/Themer/ThemeGrid/ThemeGrid.tsx new file mode 100644 index 0000000..bd88961 --- /dev/null +++ b/client/src/components/Settings/Themer/ThemeGrid/ThemeGrid.tsx @@ -0,0 +1,30 @@ +// Redux +import { useDispatch } from 'react-redux'; +import { bindActionCreators } from 'redux'; +import { actionCreators } from '../../../../store'; + +// Components +import { ThemePreview } from '../ThemePreview/ThemePreview'; + +// Other +import { Theme } from '../../../../interfaces'; +import classes from './ThemeGrid.module.css'; + +interface Props { + themes: Theme[]; +} + +export const ThemeGrid = ({ themes }: Props): JSX.Element => { + const dispatch = useDispatch(); + const { setTheme } = bindActionCreators(actionCreators, dispatch); + + return ( +
+ {themes.map( + (theme: Theme, idx: number): JSX.Element => ( + + ) + )} +
+ ); +}; diff --git a/client/src/components/Settings/Themer/ThemePreview.module.css b/client/src/components/Settings/Themer/ThemePreview/ThemePreview.module.css similarity index 100% rename from client/src/components/Settings/Themer/ThemePreview.module.css rename to client/src/components/Settings/Themer/ThemePreview/ThemePreview.module.css diff --git a/client/src/components/Settings/Themer/ThemePreview.tsx b/client/src/components/Settings/Themer/ThemePreview/ThemePreview.tsx similarity index 93% rename from client/src/components/Settings/Themer/ThemePreview.tsx rename to client/src/components/Settings/Themer/ThemePreview/ThemePreview.tsx index eccf872..81a48fe 100644 --- a/client/src/components/Settings/Themer/ThemePreview.tsx +++ b/client/src/components/Settings/Themer/ThemePreview/ThemePreview.tsx @@ -1,4 +1,4 @@ -import { Theme } from '../../../interfaces/Theme'; +import { Theme } from '../../../../interfaces/Theme'; import classes from './ThemePreview.module.css'; interface Props { diff --git a/client/src/components/Settings/Themer/Themer.tsx b/client/src/components/Settings/Themer/Themer.tsx index 61fbb92..4274a34 100644 --- a/client/src/components/Settings/Themer/Themer.tsx +++ b/client/src/components/Settings/Themer/Themer.tsx @@ -9,11 +9,11 @@ import { actionCreators } from '../../../store'; import { Theme, ThemeSettingsForm } from '../../../interfaces'; // Components -import { ThemePreview } from './ThemePreview'; import { Button, InputGroup, SettingsHeadline } from '../../UI'; +import { ThemeBuilder } from './ThemeBuilder/ThemeBuilder'; +import { ThemeGrid } from './ThemeGrid/ThemeGrid'; // Other -import classes from './Themer.module.css'; import { themes } from './themes.json'; import { State } from '../../../store/reducers'; import { inputHandler, themeSettingsTemplate } from '../../../utility'; @@ -25,10 +25,7 @@ export const Themer = (): JSX.Element => { } = useSelector((state: State) => state); const dispatch = useDispatch(); - const { setTheme, updateConfig } = bindActionCreators( - actionCreators, - dispatch - ); + const { updateConfig } = bindActionCreators(actionCreators, dispatch); // Initial state const [formData, setFormData] = useState( @@ -65,14 +62,11 @@ export const Themer = (): JSX.Element => { return ( - -
- {themes.map( - (theme: Theme, idx: number): JSX.Element => ( - - ) - )} -
+ + + + + {isAuthenticated && ( From e427fbf54c85f9fde185e427fcf4b3eedd923be4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Malak?= Date: Wed, 23 Mar 2022 14:13:14 +0100 Subject: [PATCH 10/33] Added theme string normalization to initial process. Added getThemes controller --- controllers/themes/getThemes.js | 17 ++++ controllers/themes/index.js | 3 + utils/init/index.js | 2 + utils/init/initialFiles.json | 136 ++++++++++++++++++++++++++++++++ utils/init/normalizeTheme.js | 28 +++++++ utils/init/themes.json | 124 +++++++++++++++++++++++++++++ 6 files changed, 310 insertions(+) create mode 100644 controllers/themes/getThemes.js create mode 100644 controllers/themes/index.js create mode 100644 utils/init/normalizeTheme.js create mode 100644 utils/init/themes.json diff --git a/controllers/themes/getThemes.js b/controllers/themes/getThemes.js new file mode 100644 index 0000000..af3ff13 --- /dev/null +++ b/controllers/themes/getThemes.js @@ -0,0 +1,17 @@ +const asyncWrapper = require('../../middleware/asyncWrapper'); +const File = require('../../utils/File'); + +// @desc Get themes file +// @route GET /api/themes +// @access Public +const getThemes = asyncWrapper(async (req, res, next) => { + const file = new File('data/themes.json'); + const content = JSON.parse(file.read()); + + res.status(200).json({ + success: true, + data: content.themes, + }); +}); + +module.exports = getThemes; diff --git a/controllers/themes/index.js b/controllers/themes/index.js new file mode 100644 index 0000000..84cc8e8 --- /dev/null +++ b/controllers/themes/index.js @@ -0,0 +1,3 @@ +module.exports = { + getThemes: require('./getThemes'), +}; diff --git a/utils/init/index.js b/utils/init/index.js index 4ff3e3b..66c97cf 100644 --- a/utils/init/index.js +++ b/utils/init/index.js @@ -1,11 +1,13 @@ const initConfig = require('./initConfig'); const initFiles = require('./initFiles'); const initDockerSecrets = require('./initDockerSecrets'); +const normalizeTheme = require('./normalizeTheme'); const initApp = async () => { initDockerSecrets(); await initFiles(); await initConfig(); + await normalizeTheme(); }; module.exports = initApp; diff --git a/utils/init/initialFiles.json b/utils/init/initialFiles.json index 42354d7..2168d79 100644 --- a/utils/init/initialFiles.json +++ b/utils/init/initialFiles.json @@ -27,6 +27,142 @@ "queries": [] }, "isJSON": true + }, + { + "name": "themes.json", + "msg": { + "created": "Created default theme file", + "found": "Found theme file" + }, + "paths": { + "src": "../../data", + "dest": "../../data" + }, + "template": { + "themes": [ + { + "name": "blackboard", + "colors": { + "background": "#1a1a1a", + "primary": "#FFFDEA", + "accent": "#5c5c5c" + } + }, + { + "name": "gazette", + "colors": { + "background": "#F2F7FF", + "primary": "#000000", + "accent": "#5c5c5c" + } + }, + { + "name": "espresso", + "colors": { + "background": "#21211F", + "primary": "#D1B59A", + "accent": "#4E4E4E" + } + }, + { + "name": "cab", + "colors": { + "background": "#F6D305", + "primary": "#1F1F1F", + "accent": "#424242" + } + }, + { + "name": "cloud", + "colors": { + "background": "#f1f2f0", + "primary": "#35342f", + "accent": "#37bbe4" + } + }, + { + "name": "lime", + "colors": { + "background": "#263238", + "primary": "#AABBC3", + "accent": "#aeea00" + } + }, + { + "name": "white", + "colors": { + "background": "#ffffff", + "primary": "#222222", + "accent": "#dddddd" + } + }, + { + "name": "tron", + "colors": { + "background": "#242B33", + "primary": "#EFFBFF", + "accent": "#6EE2FF" + } + }, + { + "name": "blues", + "colors": { + "background": "#2B2C56", + "primary": "#EFF1FC", + "accent": "#6677EB" + } + }, + { + "name": "passion", + "colors": { + "background": "#f5f5f5", + "primary": "#12005e", + "accent": "#8e24aa" + } + }, + { + "name": "chalk", + "colors": { + "background": "#263238", + "primary": "#AABBC3", + "accent": "#FF869A" + } + }, + { + "name": "paper", + "colors": { + "background": "#F8F6F1", + "primary": "#4C432E", + "accent": "#AA9A73" + } + }, + { + "name": "neon", + "colors": { + "background": "#091833", + "primary": "#EFFBFF", + "accent": "#ea00d9" + } + }, + { + "name": "pumpkin", + "colors": { + "background": "#2d3436", + "primary": "#EFFBFF", + "accent": "#ffa500" + } + }, + { + "name": "onedark", + "colors": { + "background": "#282c34", + "primary": "#dfd9d6", + "accent": "#98c379" + } + } + ] + }, + "isJSON": true } ] } diff --git a/utils/init/normalizeTheme.js b/utils/init/normalizeTheme.js new file mode 100644 index 0000000..272db52 --- /dev/null +++ b/utils/init/normalizeTheme.js @@ -0,0 +1,28 @@ +const { readFile, writeFile } = require('fs/promises'); + +const normalizeTheme = async () => { + // open main config file + const configFile = await readFile('data/config.json', 'utf8'); + const config = JSON.parse(configFile); + + // open default themes file + const themesFile = await readFile('utils/init/themes.json', 'utf8'); + const { themes } = JSON.parse(themesFile); + + // find theme + const theme = themes.find((t) => t.name === config.defaultTheme); + + if (theme) { + // save theme in new format + // PAB - primary;accent;background + const { primary: p, accent: a, background: b } = theme.colors; + const normalizedTheme = `${p};${a};${b}`; + + await writeFile( + 'data/config.json', + JSON.stringify({ ...config, defaultTheme: normalizedTheme }) + ); + } +}; + +module.exports = normalizeTheme; diff --git a/utils/init/themes.json b/utils/init/themes.json new file mode 100644 index 0000000..f3b12bd --- /dev/null +++ b/utils/init/themes.json @@ -0,0 +1,124 @@ +{ + "themes": [ + { + "name": "blackboard", + "colors": { + "background": "#1a1a1a", + "primary": "#FFFDEA", + "accent": "#5c5c5c" + } + }, + { + "name": "gazette", + "colors": { + "background": "#F2F7FF", + "primary": "#000000", + "accent": "#5c5c5c" + } + }, + { + "name": "espresso", + "colors": { + "background": "#21211F", + "primary": "#D1B59A", + "accent": "#4E4E4E" + } + }, + { + "name": "cab", + "colors": { + "background": "#F6D305", + "primary": "#1F1F1F", + "accent": "#424242" + } + }, + { + "name": "cloud", + "colors": { + "background": "#f1f2f0", + "primary": "#35342f", + "accent": "#37bbe4" + } + }, + { + "name": "lime", + "colors": { + "background": "#263238", + "primary": "#AABBC3", + "accent": "#aeea00" + } + }, + { + "name": "white", + "colors": { + "background": "#ffffff", + "primary": "#222222", + "accent": "#dddddd" + } + }, + { + "name": "tron", + "colors": { + "background": "#242B33", + "primary": "#EFFBFF", + "accent": "#6EE2FF" + } + }, + { + "name": "blues", + "colors": { + "background": "#2B2C56", + "primary": "#EFF1FC", + "accent": "#6677EB" + } + }, + { + "name": "passion", + "colors": { + "background": "#f5f5f5", + "primary": "#12005e", + "accent": "#8e24aa" + } + }, + { + "name": "chalk", + "colors": { + "background": "#263238", + "primary": "#AABBC3", + "accent": "#FF869A" + } + }, + { + "name": "paper", + "colors": { + "background": "#F8F6F1", + "primary": "#4C432E", + "accent": "#AA9A73" + } + }, + { + "name": "neon", + "colors": { + "background": "#091833", + "primary": "#EFFBFF", + "accent": "#ea00d9" + } + }, + { + "name": "pumpkin", + "colors": { + "background": "#2d3436", + "primary": "#EFFBFF", + "accent": "#ffa500" + } + }, + { + "name": "onedark", + "colors": { + "background": "#282c34", + "primary": "#dfd9d6", + "accent": "#98c379" + } + } + ] +} From 89bd92187535d9c65b57f016d3239d0bece7e860 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Malak?= Date: Wed, 23 Mar 2022 14:49:35 +0100 Subject: [PATCH 11/33] Changed how theme is set and stored on client --- client/src/App.tsx | 11 +- .../Settings/Themer/ThemeGrid/ThemeGrid.tsx | 10 +- .../Themer/ThemePreview/ThemePreview.tsx | 26 ++-- .../src/components/Settings/Themer/Themer.tsx | 10 +- .../components/Settings/Themer/themes.json | 124 ------------------ client/src/interfaces/Theme.ts | 14 +- client/src/store/action-creators/theme.ts | 44 ++++--- client/src/store/action-types/index.ts | 1 + client/src/store/actions/theme.ts | 6 +- client/src/store/reducers/theme.ts | 16 +-- client/src/utility/index.ts | 1 + client/src/utility/parseTheme.ts | 20 +++ 12 files changed, 92 insertions(+), 191 deletions(-) delete mode 100644 client/src/components/Settings/Themer/themes.json create mode 100644 client/src/utility/parseTheme.ts diff --git a/client/src/App.tsx b/client/src/App.tsx index 68faaea..e302c57 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -10,7 +10,7 @@ import { actionCreators, store } from './store'; import { State } from './store/reducers'; // Utils -import { checkVersion, decodeToken } from './utility'; +import { checkVersion, decodeToken, parsePABToTheme } from './utility'; // Routes import { Home } from './components/Home/Home'; @@ -31,7 +31,7 @@ export const App = (): JSX.Element => { const { config, loading } = useSelector((state: State) => state.config); const dispath = useDispatch(); - const { fetchQueries, setTheme, logout, createNotification } = + const { fetchQueries, setTheme, logout, createNotification, fetchThemes } = bindActionCreators(actionCreators, dispath); useEffect(() => { @@ -51,9 +51,12 @@ export const App = (): JSX.Element => { } }, 1000); + // load themes + fetchThemes(); + // set user theme if present if (localStorage.theme) { - setTheme(localStorage.theme); + setTheme(parsePABToTheme(localStorage.theme)); } // check for updated @@ -68,7 +71,7 @@ export const App = (): JSX.Element => { // If there is no user theme, set the default one useEffect(() => { if (!loading && !localStorage.theme) { - setTheme(config.defaultTheme, false); + setTheme(parsePABToTheme(config.defaultTheme), false); } }, [loading]); diff --git a/client/src/components/Settings/Themer/ThemeGrid/ThemeGrid.tsx b/client/src/components/Settings/Themer/ThemeGrid/ThemeGrid.tsx index bd88961..fbf5dab 100644 --- a/client/src/components/Settings/Themer/ThemeGrid/ThemeGrid.tsx +++ b/client/src/components/Settings/Themer/ThemeGrid/ThemeGrid.tsx @@ -1,8 +1,3 @@ -// Redux -import { useDispatch } from 'react-redux'; -import { bindActionCreators } from 'redux'; -import { actionCreators } from '../../../../store'; - // Components import { ThemePreview } from '../ThemePreview/ThemePreview'; @@ -15,14 +10,11 @@ interface Props { } export const ThemeGrid = ({ themes }: Props): JSX.Element => { - const dispatch = useDispatch(); - const { setTheme } = bindActionCreators(actionCreators, dispatch); - return (
{themes.map( (theme: Theme, idx: number): JSX.Element => ( - + ) )}
diff --git a/client/src/components/Settings/Themer/ThemePreview/ThemePreview.tsx b/client/src/components/Settings/Themer/ThemePreview/ThemePreview.tsx index 81a48fe..ccbb42e 100644 --- a/client/src/components/Settings/Themer/ThemePreview/ThemePreview.tsx +++ b/client/src/components/Settings/Themer/ThemePreview/ThemePreview.tsx @@ -1,32 +1,38 @@ +// Redux +import { useDispatch } from 'react-redux'; +import { bindActionCreators } from 'redux'; +import { actionCreators } from '../../../../store'; + +// Other import { Theme } from '../../../../interfaces/Theme'; import classes from './ThemePreview.module.css'; interface Props { theme: Theme; - applyTheme: Function; } -export const ThemePreview = (props: Props): JSX.Element => { +export const ThemePreview = ({ + theme: { colors, name }, +}: Props): JSX.Element => { + const { setTheme } = bindActionCreators(actionCreators, useDispatch()); + return ( -
props.applyTheme(props.theme.name)} - > +
setTheme(colors)}>
-

{props.theme.name}

+

{name}

); }; diff --git a/client/src/components/Settings/Themer/Themer.tsx b/client/src/components/Settings/Themer/Themer.tsx index 4274a34..f8de5ec 100644 --- a/client/src/components/Settings/Themer/Themer.tsx +++ b/client/src/components/Settings/Themer/Themer.tsx @@ -9,12 +9,11 @@ import { actionCreators } from '../../../store'; import { Theme, ThemeSettingsForm } from '../../../interfaces'; // Components -import { Button, InputGroup, SettingsHeadline } from '../../UI'; +import { Button, InputGroup, SettingsHeadline, Spinner } from '../../UI'; import { ThemeBuilder } from './ThemeBuilder/ThemeBuilder'; import { ThemeGrid } from './ThemeGrid/ThemeGrid'; // Other -import { themes } from './themes.json'; import { State } from '../../../store/reducers'; import { inputHandler, themeSettingsTemplate } from '../../../utility'; @@ -22,6 +21,7 @@ export const Themer = (): JSX.Element => { const { auth: { isAuthenticated }, config: { loading, config }, + theme: { themes }, } = useSelector((state: State) => state); const dispatch = useDispatch(); @@ -63,10 +63,10 @@ export const Themer = (): JSX.Element => { return ( - + {!themes.length ? : } - - + {/* + */} {isAuthenticated && ( diff --git a/client/src/components/Settings/Themer/themes.json b/client/src/components/Settings/Themer/themes.json deleted file mode 100644 index f3b12bd..0000000 --- a/client/src/components/Settings/Themer/themes.json +++ /dev/null @@ -1,124 +0,0 @@ -{ - "themes": [ - { - "name": "blackboard", - "colors": { - "background": "#1a1a1a", - "primary": "#FFFDEA", - "accent": "#5c5c5c" - } - }, - { - "name": "gazette", - "colors": { - "background": "#F2F7FF", - "primary": "#000000", - "accent": "#5c5c5c" - } - }, - { - "name": "espresso", - "colors": { - "background": "#21211F", - "primary": "#D1B59A", - "accent": "#4E4E4E" - } - }, - { - "name": "cab", - "colors": { - "background": "#F6D305", - "primary": "#1F1F1F", - "accent": "#424242" - } - }, - { - "name": "cloud", - "colors": { - "background": "#f1f2f0", - "primary": "#35342f", - "accent": "#37bbe4" - } - }, - { - "name": "lime", - "colors": { - "background": "#263238", - "primary": "#AABBC3", - "accent": "#aeea00" - } - }, - { - "name": "white", - "colors": { - "background": "#ffffff", - "primary": "#222222", - "accent": "#dddddd" - } - }, - { - "name": "tron", - "colors": { - "background": "#242B33", - "primary": "#EFFBFF", - "accent": "#6EE2FF" - } - }, - { - "name": "blues", - "colors": { - "background": "#2B2C56", - "primary": "#EFF1FC", - "accent": "#6677EB" - } - }, - { - "name": "passion", - "colors": { - "background": "#f5f5f5", - "primary": "#12005e", - "accent": "#8e24aa" - } - }, - { - "name": "chalk", - "colors": { - "background": "#263238", - "primary": "#AABBC3", - "accent": "#FF869A" - } - }, - { - "name": "paper", - "colors": { - "background": "#F8F6F1", - "primary": "#4C432E", - "accent": "#AA9A73" - } - }, - { - "name": "neon", - "colors": { - "background": "#091833", - "primary": "#EFFBFF", - "accent": "#ea00d9" - } - }, - { - "name": "pumpkin", - "colors": { - "background": "#2d3436", - "primary": "#EFFBFF", - "accent": "#ffa500" - } - }, - { - "name": "onedark", - "colors": { - "background": "#282c34", - "primary": "#dfd9d6", - "accent": "#98c379" - } - } - ] -} diff --git a/client/src/interfaces/Theme.ts b/client/src/interfaces/Theme.ts index 9753427..4bf5bfd 100644 --- a/client/src/interfaces/Theme.ts +++ b/client/src/interfaces/Theme.ts @@ -1,8 +1,10 @@ +export interface ThemeColors { + background: string; + primary: string; + accent: string; +} + export interface Theme { name: string; - colors: { - background: string; - primary: string; - accent: string; - } -} \ No newline at end of file + colors: ThemeColors; +} diff --git a/client/src/store/action-creators/theme.ts b/client/src/store/action-creators/theme.ts index 8eb6fef..87bf184 100644 --- a/client/src/store/action-creators/theme.ts +++ b/client/src/store/action-creators/theme.ts @@ -1,30 +1,32 @@ import { Dispatch } from 'redux'; -import { SetThemeAction } from '../actions/theme'; +import { FetchThemesAction, SetThemeAction } from '../actions/theme'; import { ActionType } from '../action-types'; -import { Theme } from '../../interfaces/Theme'; -import { themes } from '../../components/Settings/Themer/themes.json'; +import { Theme, ApiResponse, ThemeColors } from '../../interfaces'; +import { parseThemeToPAB } from '../../utility'; +import axios from 'axios'; export const setTheme = - (name: string, remeberTheme: boolean = true) => + (colors: ThemeColors, remeberTheme: boolean = true) => (dispatch: Dispatch) => { - const theme = themes.find((theme) => theme.name === name); + if (remeberTheme) { + localStorage.setItem('theme', parseThemeToPAB(colors)); + } - if (theme) { - if (remeberTheme) { - localStorage.setItem('theme', name); - } - - loadTheme(theme); - - dispatch({ - type: ActionType.setTheme, - payload: theme, - }); + for (const [key, value] of Object.entries(colors)) { + document.body.style.setProperty(`--color-${key}`, value); } }; -export const loadTheme = (theme: Theme): void => { - for (const [key, value] of Object.entries(theme.colors)) { - document.body.style.setProperty(`--color-${key}`, value); - } -}; +export const fetchThemes = + () => async (dispatch: Dispatch) => { + try { + const res = await axios.get>('/api/themes'); + + dispatch({ + type: ActionType.fetchThemes, + payload: res.data.data, + }); + } catch (err) { + console.log(err); + } + }; diff --git a/client/src/store/action-types/index.ts b/client/src/store/action-types/index.ts index 4be159f..601308b 100644 --- a/client/src/store/action-types/index.ts +++ b/client/src/store/action-types/index.ts @@ -1,6 +1,7 @@ export enum ActionType { // THEME setTheme = 'SET_THEME', + fetchThemes = 'FETCH_THEMES', // CONFIG getConfig = 'GET_CONFIG', updateConfig = 'UPDATE_CONFIG', diff --git a/client/src/store/actions/theme.ts b/client/src/store/actions/theme.ts index 036b1a3..6d40d21 100644 --- a/client/src/store/actions/theme.ts +++ b/client/src/store/actions/theme.ts @@ -3,5 +3,9 @@ import { Theme } from '../../interfaces'; export interface SetThemeAction { type: ActionType.setTheme; - payload: Theme; +} + +export interface FetchThemesAction { + type: ActionType.fetchThemes; + payload: Theme[]; } diff --git a/client/src/store/reducers/theme.ts b/client/src/store/reducers/theme.ts index 6db29fe..c204e20 100644 --- a/client/src/store/reducers/theme.ts +++ b/client/src/store/reducers/theme.ts @@ -3,18 +3,11 @@ import { ActionType } from '../action-types'; import { Theme } from '../../interfaces/Theme'; interface ThemeState { - theme: Theme; + themes: Theme[]; } const initialState: ThemeState = { - theme: { - name: 'tron', - colors: { - background: '#242B33', - primary: '#EFFBFF', - accent: '#6EE2FF', - }, - }, + themes: [], }; export const themeReducer = ( @@ -22,8 +15,9 @@ export const themeReducer = ( action: Action ): ThemeState => { switch (action.type) { - case ActionType.setTheme: - return { theme: action.payload }; + case ActionType.fetchThemes: { + return { themes: action.payload }; + } default: return state; diff --git a/client/src/utility/index.ts b/client/src/utility/index.ts index 7358da4..990db06 100644 --- a/client/src/utility/index.ts +++ b/client/src/utility/index.ts @@ -12,3 +12,4 @@ export * from './parseTime'; export * from './decodeToken'; export * from './applyAuth'; export * from './escapeRegex'; +export * from './parseTheme'; diff --git a/client/src/utility/parseTheme.ts b/client/src/utility/parseTheme.ts new file mode 100644 index 0000000..eaa800f --- /dev/null +++ b/client/src/utility/parseTheme.ts @@ -0,0 +1,20 @@ +import { ThemeColors } from '../interfaces'; + +// parse theme in PAB (primary;accent;background) format to theme colors object +export const parsePABToTheme = (themeStr: string): ThemeColors => { + const [primary, accent, background] = themeStr.split(';'); + + return { + primary, + accent, + background, + }; +}; + +export const parseThemeToPAB = ({ + primary: p, + accent: a, + background: b, +}: ThemeColors): string => { + return `${p};${a};${b}`; +}; From 48e28b9abdad609a39a37f21e86a839a560f26f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Malak?= Date: Wed, 23 Mar 2022 16:13:34 +0100 Subject: [PATCH 12/33] Added user themes section to Theme settings --- .../ThemeBuilder/ThemeBuilder.module.css | 7 +++ .../Themer/ThemeBuilder/ThemeBuilder.tsx | 22 ++++++++- .../src/components/Settings/Themer/Themer.tsx | 26 +++++++---- client/src/interfaces/Theme.ts | 1 + client/src/store/reducers/theme.ts | 14 +++++- client/src/utility/arrayPartition.ts | 11 +++++ client/src/utility/index.ts | 1 + utils/Logger.js | 8 ++-- utils/init/initialFiles.json | 45 ++++++++++++------- utils/init/themes.json | 45 ++++++++++++------- 10 files changed, 136 insertions(+), 44 deletions(-) create mode 100644 client/src/components/Settings/Themer/ThemeBuilder/ThemeBuilder.module.css create mode 100644 client/src/utility/arrayPartition.ts diff --git a/client/src/components/Settings/Themer/ThemeBuilder/ThemeBuilder.module.css b/client/src/components/Settings/Themer/ThemeBuilder/ThemeBuilder.module.css new file mode 100644 index 0000000..2c63e13 --- /dev/null +++ b/client/src/components/Settings/Themer/ThemeBuilder/ThemeBuilder.module.css @@ -0,0 +1,7 @@ +.ThemeBuilder { + margin-bottom: 30px; +} + +.Buttons button:not(:last-child) { + margin-right: 10px; +} diff --git a/client/src/components/Settings/Themer/ThemeBuilder/ThemeBuilder.tsx b/client/src/components/Settings/Themer/ThemeBuilder/ThemeBuilder.tsx index c95eb38..0762f13 100644 --- a/client/src/components/Settings/Themer/ThemeBuilder/ThemeBuilder.tsx +++ b/client/src/components/Settings/Themer/ThemeBuilder/ThemeBuilder.tsx @@ -1,3 +1,21 @@ -export const ThemeBuilder = (): JSX.Element => { - return

theme builder

; +import { Theme } from '../../../../interfaces'; +import { Button } from '../../../UI'; +import { ThemeGrid } from '../ThemeGrid/ThemeGrid'; +import classes from './ThemeBuilder.module.css'; + +interface Props { + themes: Theme[]; +} + +export const ThemeBuilder = ({ themes }: Props): JSX.Element => { + return ( +
+ + +
+ + {themes.length && } +
+
+ ); }; diff --git a/client/src/components/Settings/Themer/Themer.tsx b/client/src/components/Settings/Themer/Themer.tsx index f8de5ec..3bcaa2e 100644 --- a/client/src/components/Settings/Themer/Themer.tsx +++ b/client/src/components/Settings/Themer/Themer.tsx @@ -15,13 +15,17 @@ import { ThemeGrid } from './ThemeGrid/ThemeGrid'; // Other import { State } from '../../../store/reducers'; -import { inputHandler, themeSettingsTemplate } from '../../../utility'; +import { + inputHandler, + parseThemeToPAB, + themeSettingsTemplate, +} from '../../../utility'; export const Themer = (): JSX.Element => { const { auth: { isAuthenticated }, config: { loading, config }, - theme: { themes }, + theme: { themes, userThemes }, } = useSelector((state: State) => state); const dispatch = useDispatch(); @@ -44,7 +48,7 @@ export const Themer = (): JSX.Element => { e.preventDefault(); // Save settings - await updateConfig(formData); + await updateConfig({ ...formData }); }; // Input handler @@ -65,8 +69,14 @@ export const Themer = (): JSX.Element => { {!themes.length ? : } - {/* - */} + {!userThemes.length ? ( + isAuthenticated && 'auth and empty' + ) : ( + + + + + )} {isAuthenticated && ( @@ -79,9 +89,9 @@ export const Themer = (): JSX.Element => { value={formData.defaultTheme} onChange={(e) => inputChangeHandler(e)} > - {themes.map((theme: Theme, idx) => ( - ))} diff --git a/client/src/interfaces/Theme.ts b/client/src/interfaces/Theme.ts index 4bf5bfd..e319ef1 100644 --- a/client/src/interfaces/Theme.ts +++ b/client/src/interfaces/Theme.ts @@ -7,4 +7,5 @@ export interface ThemeColors { export interface Theme { name: string; colors: ThemeColors; + isCustom: boolean; } diff --git a/client/src/store/reducers/theme.ts b/client/src/store/reducers/theme.ts index c204e20..7738a3b 100644 --- a/client/src/store/reducers/theme.ts +++ b/client/src/store/reducers/theme.ts @@ -1,13 +1,16 @@ import { Action } from '../actions'; import { ActionType } from '../action-types'; import { Theme } from '../../interfaces/Theme'; +import { arrayPartition } from '../../utility'; interface ThemeState { themes: Theme[]; + userThemes: Theme[]; } const initialState: ThemeState = { themes: [], + userThemes: [], }; export const themeReducer = ( @@ -16,7 +19,16 @@ export const themeReducer = ( ): ThemeState => { switch (action.type) { case ActionType.fetchThemes: { - return { themes: action.payload }; + const [themes, userThemes] = arrayPartition( + action.payload, + (e) => !e.isCustom + ); + + return { + ...state, + themes, + userThemes, + }; } default: diff --git a/client/src/utility/arrayPartition.ts b/client/src/utility/arrayPartition.ts new file mode 100644 index 0000000..eae67c3 --- /dev/null +++ b/client/src/utility/arrayPartition.ts @@ -0,0 +1,11 @@ +export const arrayPartition = ( + arr: T[], + isValid: (e: T) => boolean +): T[][] => { + let pass: T[] = []; + let fail: T[] = []; + + arr.forEach((e) => (isValid(e) ? pass : fail).push(e)); + + return [pass, fail]; +}; diff --git a/client/src/utility/index.ts b/client/src/utility/index.ts index 990db06..0d002bc 100644 --- a/client/src/utility/index.ts +++ b/client/src/utility/index.ts @@ -13,3 +13,4 @@ export * from './decodeToken'; export * from './applyAuth'; export * from './escapeRegex'; export * from './parseTheme'; +export * from './arrayPartition'; diff --git a/utils/Logger.js b/utils/Logger.js index 1d1deef..411b39f 100644 --- a/utils/Logger.js +++ b/utils/Logger.js @@ -1,6 +1,6 @@ class Logger { log(message, level = 'INFO') { - console.log(`[${this.generateTimestamp()}] [${level}] ${message}`) + console.log(`[${this.generateTimestamp()}] [${level}] ${message}`); } generateTimestamp() { @@ -20,7 +20,9 @@ class Logger { // Timezone const tz = -d.getTimezoneOffset() / 60; - return `${year}-${month}-${day} ${hour}:${minutes}:${seconds}.${miliseconds} UTC${tz >= 0 ? '+' + tz : tz}`; + return `${year}-${month}-${day} ${hour}:${minutes}:${seconds}.${miliseconds} UTC${ + tz >= 0 ? '+' + tz : tz + }`; } parseDate(date, ms = false) { @@ -36,4 +38,4 @@ class Logger { } } -module.exports = Logger; \ No newline at end of file +module.exports = Logger; diff --git a/utils/init/initialFiles.json b/utils/init/initialFiles.json index 2168d79..43b68d5 100644 --- a/utils/init/initialFiles.json +++ b/utils/init/initialFiles.json @@ -46,7 +46,8 @@ "background": "#1a1a1a", "primary": "#FFFDEA", "accent": "#5c5c5c" - } + }, + "isCustom": false }, { "name": "gazette", @@ -54,7 +55,8 @@ "background": "#F2F7FF", "primary": "#000000", "accent": "#5c5c5c" - } + }, + "isCustom": false }, { "name": "espresso", @@ -62,7 +64,8 @@ "background": "#21211F", "primary": "#D1B59A", "accent": "#4E4E4E" - } + }, + "isCustom": false }, { "name": "cab", @@ -70,7 +73,8 @@ "background": "#F6D305", "primary": "#1F1F1F", "accent": "#424242" - } + }, + "isCustom": false }, { "name": "cloud", @@ -78,7 +82,8 @@ "background": "#f1f2f0", "primary": "#35342f", "accent": "#37bbe4" - } + }, + "isCustom": false }, { "name": "lime", @@ -86,7 +91,8 @@ "background": "#263238", "primary": "#AABBC3", "accent": "#aeea00" - } + }, + "isCustom": false }, { "name": "white", @@ -94,7 +100,8 @@ "background": "#ffffff", "primary": "#222222", "accent": "#dddddd" - } + }, + "isCustom": false }, { "name": "tron", @@ -102,7 +109,8 @@ "background": "#242B33", "primary": "#EFFBFF", "accent": "#6EE2FF" - } + }, + "isCustom": false }, { "name": "blues", @@ -110,7 +118,8 @@ "background": "#2B2C56", "primary": "#EFF1FC", "accent": "#6677EB" - } + }, + "isCustom": false }, { "name": "passion", @@ -118,7 +127,8 @@ "background": "#f5f5f5", "primary": "#12005e", "accent": "#8e24aa" - } + }, + "isCustom": false }, { "name": "chalk", @@ -126,7 +136,8 @@ "background": "#263238", "primary": "#AABBC3", "accent": "#FF869A" - } + }, + "isCustom": false }, { "name": "paper", @@ -134,7 +145,8 @@ "background": "#F8F6F1", "primary": "#4C432E", "accent": "#AA9A73" - } + }, + "isCustom": false }, { "name": "neon", @@ -142,7 +154,8 @@ "background": "#091833", "primary": "#EFFBFF", "accent": "#ea00d9" - } + }, + "isCustom": false }, { "name": "pumpkin", @@ -150,7 +163,8 @@ "background": "#2d3436", "primary": "#EFFBFF", "accent": "#ffa500" - } + }, + "isCustom": false }, { "name": "onedark", @@ -158,7 +172,8 @@ "background": "#282c34", "primary": "#dfd9d6", "accent": "#98c379" - } + }, + "isCustom": false } ] }, diff --git a/utils/init/themes.json b/utils/init/themes.json index f3b12bd..685a81e 100644 --- a/utils/init/themes.json +++ b/utils/init/themes.json @@ -6,7 +6,8 @@ "background": "#1a1a1a", "primary": "#FFFDEA", "accent": "#5c5c5c" - } + }, + "isCustom": false }, { "name": "gazette", @@ -14,7 +15,8 @@ "background": "#F2F7FF", "primary": "#000000", "accent": "#5c5c5c" - } + }, + "isCustom": false }, { "name": "espresso", @@ -22,7 +24,8 @@ "background": "#21211F", "primary": "#D1B59A", "accent": "#4E4E4E" - } + }, + "isCustom": false }, { "name": "cab", @@ -30,7 +33,8 @@ "background": "#F6D305", "primary": "#1F1F1F", "accent": "#424242" - } + }, + "isCustom": false }, { "name": "cloud", @@ -38,7 +42,8 @@ "background": "#f1f2f0", "primary": "#35342f", "accent": "#37bbe4" - } + }, + "isCustom": false }, { "name": "lime", @@ -46,7 +51,8 @@ "background": "#263238", "primary": "#AABBC3", "accent": "#aeea00" - } + }, + "isCustom": false }, { "name": "white", @@ -54,7 +60,8 @@ "background": "#ffffff", "primary": "#222222", "accent": "#dddddd" - } + }, + "isCustom": false }, { "name": "tron", @@ -62,7 +69,8 @@ "background": "#242B33", "primary": "#EFFBFF", "accent": "#6EE2FF" - } + }, + "isCustom": false }, { "name": "blues", @@ -70,7 +78,8 @@ "background": "#2B2C56", "primary": "#EFF1FC", "accent": "#6677EB" - } + }, + "isCustom": false }, { "name": "passion", @@ -78,7 +87,8 @@ "background": "#f5f5f5", "primary": "#12005e", "accent": "#8e24aa" - } + }, + "isCustom": false }, { "name": "chalk", @@ -86,7 +96,8 @@ "background": "#263238", "primary": "#AABBC3", "accent": "#FF869A" - } + }, + "isCustom": false }, { "name": "paper", @@ -94,7 +105,8 @@ "background": "#F8F6F1", "primary": "#4C432E", "accent": "#AA9A73" - } + }, + "isCustom": false }, { "name": "neon", @@ -102,7 +114,8 @@ "background": "#091833", "primary": "#EFFBFF", "accent": "#ea00d9" - } + }, + "isCustom": false }, { "name": "pumpkin", @@ -110,7 +123,8 @@ "background": "#2d3436", "primary": "#EFFBFF", "accent": "#ffa500" - } + }, + "isCustom": false }, { "name": "onedark", @@ -118,7 +132,8 @@ "background": "#282c34", "primary": "#dfd9d6", "accent": "#98c379" - } + }, + "isCustom": false } ] } From b8af178cbfc63e2b91c40721d76ef61695fe4a9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Malak?= Date: Thu, 24 Mar 2022 14:48:10 +0100 Subject: [PATCH 13/33] API routes to get and add themes --- api.js | 1 + controllers/queries/addQuery.js | 7 +++++++ controllers/themes/addTheme.js | 28 ++++++++++++++++++++++++++++ controllers/themes/index.js | 1 + routes/queries.js | 13 +++++++++++-- routes/themes.js | 19 +++++++++++++++++++ 6 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 controllers/themes/addTheme.js create mode 100644 routes/themes.js diff --git a/api.js b/api.js index 840529a..45be359 100644 --- a/api.js +++ b/api.js @@ -22,6 +22,7 @@ api.use('/api/categories', require('./routes/category')); api.use('/api/bookmarks', require('./routes/bookmark')); api.use('/api/queries', require('./routes/queries')); api.use('/api/auth', require('./routes/auth')); +api.use('/api/themes', require('./routes/themes')); // Custom error handler api.use(errorHandler); diff --git a/controllers/queries/addQuery.js b/controllers/queries/addQuery.js index cd61c67..9db41a8 100644 --- a/controllers/queries/addQuery.js +++ b/controllers/queries/addQuery.js @@ -1,4 +1,5 @@ const asyncWrapper = require('../../middleware/asyncWrapper'); +const ErrorResponse = require('../../utils/ErrorResponse'); const File = require('../../utils/File'); // @desc Add custom search query @@ -8,6 +9,12 @@ const addQuery = asyncWrapper(async (req, res, next) => { const file = new File('data/customQueries.json'); let content = JSON.parse(file.read()); + const prefixes = content.queries.map((q) => q.prefix); + + if (prefixes.includes(req.body.prefix)) { + return next(new ErrorResponse('Prefix must be unique', 400)); + } + // Add new query content.queries.push(req.body); file.write(content, true); diff --git a/controllers/themes/addTheme.js b/controllers/themes/addTheme.js new file mode 100644 index 0000000..1d2cf0c --- /dev/null +++ b/controllers/themes/addTheme.js @@ -0,0 +1,28 @@ +const asyncWrapper = require('../../middleware/asyncWrapper'); +const ErrorResponse = require('../../utils/ErrorResponse'); +const File = require('../../utils/File'); + +// @desc Create new theme +// @route POST /api/themes +// @access Private +const addTheme = asyncWrapper(async (req, res, next) => { + const file = new File('data/themes.json'); + let content = JSON.parse(file.read()); + + const themeNames = content.themes.map((t) => t.name); + + if (themeNames.includes(req.body.name)) { + return next(new ErrorResponse('Name must be unique', 400)); + } + + // Add new theme + content.themes.push(req.body); + file.write(content, true); + + res.status(201).json({ + success: true, + data: req.body, + }); +}); + +module.exports = addTheme; diff --git a/controllers/themes/index.js b/controllers/themes/index.js index 84cc8e8..7ec3b92 100644 --- a/controllers/themes/index.js +++ b/controllers/themes/index.js @@ -1,3 +1,4 @@ module.exports = { getThemes: require('./getThemes'), + addTheme: require('./addTheme'), }; diff --git a/routes/queries.js b/routes/queries.js index 2262611..ec15790 100644 --- a/routes/queries.js +++ b/routes/queries.js @@ -2,7 +2,7 @@ const express = require('express'); const router = express.Router(); // middleware -const { auth, requireAuth } = require('../middleware'); +const { auth, requireAuth, requireBody } = require('../middleware'); const { getQueries, @@ -11,7 +11,16 @@ const { updateQuery, } = require('../controllers/queries/'); -router.route('/').post(auth, requireAuth, addQuery).get(getQueries); +router + .route('/') + .post( + auth, + requireAuth, + requireBody(['name', 'prefix', 'template']), + addQuery + ) + .get(getQueries); + router .route('/:prefix') .delete(auth, requireAuth, deleteQuery) diff --git a/routes/themes.js b/routes/themes.js new file mode 100644 index 0000000..42d6b35 --- /dev/null +++ b/routes/themes.js @@ -0,0 +1,19 @@ +const express = require('express'); +const router = express.Router(); + +// middleware +const { auth, requireAuth, requireBody } = require('../middleware'); + +const { getThemes, addTheme } = require('../controllers/themes/'); + +router + .route('/') + .get(getThemes) + .post( + auth, + requireAuth, + requireBody(['name', 'colors', 'isCustom']), + addTheme + ); + +module.exports = router; From 378dd8e36df8e753682d647938c3eacce326b0e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Malak?= Date: Thu, 24 Mar 2022 14:56:36 +0100 Subject: [PATCH 14/33] Fixed color of weather icon when changing theme --- .../UI/Icons/WeatherIcon/WeatherIcon.tsx | 6 ++-- client/src/store/action-creators/config.ts | 12 +++++-- client/src/store/action-creators/theme.ts | 35 +++++++++++++++++++ client/src/store/actions/theme.ts | 3 +- client/src/store/reducers/theme.ts | 28 +++++++++++++-- 5 files changed, 76 insertions(+), 8 deletions(-) diff --git a/client/src/components/UI/Icons/WeatherIcon/WeatherIcon.tsx b/client/src/components/UI/Icons/WeatherIcon/WeatherIcon.tsx index 2664b47..d28aff4 100644 --- a/client/src/components/UI/Icons/WeatherIcon/WeatherIcon.tsx +++ b/client/src/components/UI/Icons/WeatherIcon/WeatherIcon.tsx @@ -10,7 +10,7 @@ interface Props { } export const WeatherIcon = (props: Props): JSX.Element => { - const { theme } = useSelector((state: State) => state.theme); + const { activeTheme } = useSelector((state: State) => state.theme); const icon = props.isDay ? new IconMapping().mapIcon(props.weatherStatusCode, TimeOfDay.day) @@ -18,7 +18,7 @@ export const WeatherIcon = (props: Props): JSX.Element => { useEffect(() => { const delay = setTimeout(() => { - const skycons = new Skycons({ color: theme.colors.accent }); + const skycons = new Skycons({ color: activeTheme.colors.accent }); skycons.add(`weather-icon`, icon); skycons.play(); }, 1); @@ -26,7 +26,7 @@ export const WeatherIcon = (props: Props): JSX.Element => { return () => { clearTimeout(delay); }; - }, [props.weatherStatusCode, icon, theme.colors.accent]); + }, [props.weatherStatusCode, icon, activeTheme.colors.accent]); return ; }; diff --git a/client/src/store/action-creators/config.ts b/client/src/store/action-creators/config.ts index 6b516f7..d019876 100644 --- a/client/src/store/action-creators/config.ts +++ b/client/src/store/action-creators/config.ts @@ -7,7 +7,7 @@ import { UpdateConfigAction, UpdateQueryAction, } from '../actions/config'; -import axios from 'axios'; +import axios, { AxiosError } from 'axios'; import { ApiResponse, Config, Query } from '../../interfaces'; import { ActionType } from '../action-types'; import { storeUIConfig, applyAuth } from '../../utility'; @@ -103,7 +103,15 @@ export const addQuery = payload: res.data.data, }); } catch (err) { - console.log(err); + const error = err as AxiosError<{ error: string }>; + + dispatch({ + type: ActionType.createNotification, + payload: { + title: 'Error', + message: error.response?.data.error, + }, + }); } }; diff --git a/client/src/store/action-creators/theme.ts b/client/src/store/action-creators/theme.ts index 87bf184..2870b6c 100644 --- a/client/src/store/action-creators/theme.ts +++ b/client/src/store/action-creators/theme.ts @@ -15,6 +15,11 @@ export const setTheme = for (const [key, value] of Object.entries(colors)) { document.body.style.setProperty(`--color-${key}`, value); } + + dispatch({ + type: ActionType.setTheme, + payload: colors, + }); }; export const fetchThemes = @@ -30,3 +35,33 @@ export const fetchThemes = console.log(err); } }; + +// export const addTheme = () => async (dispatch: Dispatch<>) => { +// try { +// // const res = await axios.post<>('/api/themes') +// } catch (err) {} +// }; + +// export const addQuery = +// (query: Query) => async (dispatch: Dispatch) => { +// try { +// const res = await axios.post>('/api/queries', query, { +// headers: applyAuth(), +// }); + +// dispatch({ +// type: ActionType.addQuery, +// payload: res.data.data, +// }); +// } catch (err) { +// const error = err as AxiosError<{ error: string }>; + +// dispatch({ +// type: ActionType.createNotification, +// payload: { +// title: 'Error', +// message: error.response?.data.error, +// }, +// }); +// } +// }; diff --git a/client/src/store/actions/theme.ts b/client/src/store/actions/theme.ts index 6d40d21..b3d3e9d 100644 --- a/client/src/store/actions/theme.ts +++ b/client/src/store/actions/theme.ts @@ -1,8 +1,9 @@ import { ActionType } from '../action-types'; -import { Theme } from '../../interfaces'; +import { Theme, ThemeColors } from '../../interfaces'; export interface SetThemeAction { type: ActionType.setTheme; + payload: ThemeColors; } export interface FetchThemesAction { diff --git a/client/src/store/reducers/theme.ts b/client/src/store/reducers/theme.ts index 7738a3b..62ccf6b 100644 --- a/client/src/store/reducers/theme.ts +++ b/client/src/store/reducers/theme.ts @@ -1,14 +1,28 @@ import { Action } from '../actions'; import { ActionType } from '../action-types'; -import { Theme } from '../../interfaces/Theme'; -import { arrayPartition } from '../../utility'; +import { Theme, ThemeColors } from '../../interfaces/Theme'; +import { arrayPartition, parsePABToTheme } from '../../utility'; interface ThemeState { + activeTheme: Theme; themes: Theme[]; userThemes: Theme[]; } +const savedTheme: ThemeColors = parsePABToTheme(localStorage.theme) || { + primary: '#effbff', + accent: '#6ee2ff', + background: '#242b33', +}; + const initialState: ThemeState = { + activeTheme: { + name: 'main', + isCustom: false, + colors: { + ...savedTheme, + }, + }, themes: [], userThemes: [], }; @@ -18,6 +32,16 @@ export const themeReducer = ( action: Action ): ThemeState => { switch (action.type) { + case ActionType.setTheme: { + return { + ...state, + activeTheme: { + ...state.activeTheme, + colors: action.payload, + }, + }; + } + case ActionType.fetchThemes: { const [themes, userThemes] = arrayPartition( action.payload, From 9ab6c65d85544758e7ad10347ca194a00b6e7dc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Malak?= Date: Thu, 24 Mar 2022 16:07:14 +0100 Subject: [PATCH 15/33] Added custom theme creator --- .../Themer/ThemeBuilder/ThemeBuilder.tsx | 58 ++++++++- .../ThemeBuilder/ThemeCreator.module.css | 6 + .../Themer/ThemeBuilder/ThemeCreator.tsx | 117 ++++++++++++++++++ .../Themer/ThemeBuilder/ThemeEditor.tsx | 13 ++ .../src/components/Settings/Themer/Themer.tsx | 4 +- .../UI/Forms/InputGroup/InputGroup.module.css | 4 + client/src/store/action-creators/theme.ts | 64 +++++----- client/src/store/action-types/index.ts | 1 + client/src/store/actions/theme.ts | 5 + client/src/store/reducers/theme.ts | 15 ++- 10 files changed, 246 insertions(+), 41 deletions(-) create mode 100644 client/src/components/Settings/Themer/ThemeBuilder/ThemeCreator.module.css create mode 100644 client/src/components/Settings/Themer/ThemeBuilder/ThemeCreator.tsx create mode 100644 client/src/components/Settings/Themer/ThemeBuilder/ThemeEditor.tsx diff --git a/client/src/components/Settings/Themer/ThemeBuilder/ThemeBuilder.tsx b/client/src/components/Settings/Themer/ThemeBuilder/ThemeBuilder.tsx index 0762f13..4ae2822 100644 --- a/client/src/components/Settings/Themer/ThemeBuilder/ThemeBuilder.tsx +++ b/client/src/components/Settings/Themer/ThemeBuilder/ThemeBuilder.tsx @@ -1,21 +1,69 @@ +import { useState } from 'react'; + +// Redux +import { useSelector } from 'react-redux'; +import { State } from '../../../../store/reducers'; + +// Other import { Theme } from '../../../../interfaces'; -import { Button } from '../../../UI'; + +// UI +import { Button, Modal } from '../../../UI'; import { ThemeGrid } from '../ThemeGrid/ThemeGrid'; import classes from './ThemeBuilder.module.css'; +import { ThemeCreator } from './ThemeCreator'; +import { ThemeEditor } from './ThemeEditor'; interface Props { themes: Theme[]; } export const ThemeBuilder = ({ themes }: Props): JSX.Element => { + const { + auth: { isAuthenticated }, + } = useSelector((state: State) => state); + + const [showModal, toggleShowModal] = useState(false); + const [isInEdit, toggleIsInEdit] = useState(false); + return (
+ {/* MODALS */} + toggleShowModal(!showModal)}> + {isInEdit ? ( + toggleShowModal(!showModal)} /> + ) : ( + toggleShowModal(!showModal)} /> + )} + + + {/* USER THEMES */} -
- - {themes.length && } -
+ {/* BUTTONS */} + {isAuthenticated && ( +
+ + + {themes.length && ( + + )} +
+ )}
); }; diff --git a/client/src/components/Settings/Themer/ThemeBuilder/ThemeCreator.module.css b/client/src/components/Settings/Themer/ThemeBuilder/ThemeCreator.module.css new file mode 100644 index 0000000..893095f --- /dev/null +++ b/client/src/components/Settings/Themer/ThemeBuilder/ThemeCreator.module.css @@ -0,0 +1,6 @@ +.ColorsContainer { + display: grid; + grid-template-columns: repeat(3, 1fr); + grid-gap: 10px; + margin-bottom: 20px; +} diff --git a/client/src/components/Settings/Themer/ThemeBuilder/ThemeCreator.tsx b/client/src/components/Settings/Themer/ThemeBuilder/ThemeCreator.tsx new file mode 100644 index 0000000..0d696e6 --- /dev/null +++ b/client/src/components/Settings/Themer/ThemeBuilder/ThemeCreator.tsx @@ -0,0 +1,117 @@ +import { ChangeEvent, FormEvent, useState } from 'react'; +import { useDispatch } from 'react-redux'; +import { bindActionCreators } from 'redux'; +import { actionCreators } from '../../../../store'; +import { Theme } from '../../../../interfaces'; +import { Button, InputGroup, ModalForm } from '../../../UI'; + +import classes from './ThemeCreator.module.css'; + +interface Props { + modalHandler: () => void; +} + +export const ThemeCreator = ({ modalHandler }: Props): JSX.Element => { + const { addTheme } = bindActionCreators(actionCreators, useDispatch()); + + const [formData, setFormData] = useState({ + name: '', + isCustom: true, + colors: { + primary: '#ffffff', + accent: '#ffffff', + background: '#ffffff', + }, + }); + + const inputChangeHandler = (e: ChangeEvent) => { + const { name, value } = e.target; + + setFormData({ + ...formData, + [name]: value, + }); + }; + + const setColor = ({ + target: { value, name }, + }: ChangeEvent) => { + setFormData({ + ...formData, + colors: { + ...formData.colors, + [name]: value, + }, + }); + }; + + const formHandler = (e: FormEvent) => { + e.preventDefault(); + + // add new theme + addTheme(formData); + + // close modal + modalHandler(); + + // clear theme name + setFormData({ ...formData, name: '' }); + }; + + return ( + + + + inputChangeHandler(e)} + /> + + +
+ + + setColor(e)} + /> + + + + + setColor(e)} + /> + + + + + setColor(e)} + /> + +
+ + +
+ ); +}; diff --git a/client/src/components/Settings/Themer/ThemeBuilder/ThemeEditor.tsx b/client/src/components/Settings/Themer/ThemeBuilder/ThemeEditor.tsx new file mode 100644 index 0000000..b899049 --- /dev/null +++ b/client/src/components/Settings/Themer/ThemeBuilder/ThemeEditor.tsx @@ -0,0 +1,13 @@ +import { ModalForm } from '../../../UI'; + +interface Props { + modalHandler: () => void; +} + +export const ThemeEditor = (props: Props): JSX.Element => { + return ( + {}} modalHandler={props.modalHandler}> +

edit

+
+ ); +}; diff --git a/client/src/components/Settings/Themer/Themer.tsx b/client/src/components/Settings/Themer/Themer.tsx index 3bcaa2e..5a7ff06 100644 --- a/client/src/components/Settings/Themer/Themer.tsx +++ b/client/src/components/Settings/Themer/Themer.tsx @@ -4,6 +4,7 @@ import { ChangeEvent, FormEvent, Fragment, useEffect, useState } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { bindActionCreators } from 'redux'; import { actionCreators } from '../../../store'; +import { State } from '../../../store/reducers'; // Typescript import { Theme, ThemeSettingsForm } from '../../../interfaces'; @@ -14,7 +15,6 @@ import { ThemeBuilder } from './ThemeBuilder/ThemeBuilder'; import { ThemeGrid } from './ThemeGrid/ThemeGrid'; // Other -import { State } from '../../../store/reducers'; import { inputHandler, parseThemeToPAB, @@ -82,7 +82,7 @@ export const Themer = (): JSX.Element => { - + { }); }; + const customThemesEl = ( + + + + + ); + return ( {!themes.length ? : } - {!userThemes.length ? ( - isAuthenticated && 'auth and empty' - ) : ( - - - - - )} + {!userThemes.length ? isAuthenticated && customThemesEl : customThemesEl} {isAuthenticated && ( diff --git a/client/src/components/UI/Modal/Modal.tsx b/client/src/components/UI/Modal/Modal.tsx index 43fb5e9..3f2a6bd 100644 --- a/client/src/components/UI/Modal/Modal.tsx +++ b/client/src/components/UI/Modal/Modal.tsx @@ -6,24 +6,32 @@ interface Props { isOpen: boolean; setIsOpen: Function; children: ReactNode; + cb?: Function; } -export const Modal = (props: Props): JSX.Element => { +export const Modal = ({ + isOpen, + setIsOpen, + children, + cb, +}: Props): JSX.Element => { const modalRef = useRef(null); const modalClasses = [ classes.Modal, - props.isOpen ? classes.ModalOpen : classes.ModalClose, + isOpen ? classes.ModalOpen : classes.ModalClose, ].join(' '); const clickHandler = (e: MouseEvent) => { if (e.target === modalRef.current) { - props.setIsOpen(false); + setIsOpen(false); + + if (cb) cb(); } }; return (
- {props.children} + {children}
); }; diff --git a/utils/init/initialFiles.json b/utils/init/initialFiles.json index 43b68d5..83d370a 100644 --- a/utils/init/initialFiles.json +++ b/utils/init/initialFiles.json @@ -174,6 +174,15 @@ "accent": "#98c379" }, "isCustom": false + }, + { + "name": "mint", + "colors": { + "background": "#282525", + "primary": "#d9d9d9", + "accent": "#50fbc2" + }, + "isCustom": false } ] }, diff --git a/utils/init/themes.json b/utils/init/themes.json index 685a81e..867fe7e 100644 --- a/utils/init/themes.json +++ b/utils/init/themes.json @@ -134,6 +134,15 @@ "accent": "#98c379" }, "isCustom": false + }, + { + "name": "mint", + "colors": { + "background": "#282525", + "primary": "#d9d9d9", + "accent": "#50fbc2" + }, + "isCustom": false } ] } From 16121ff5471c85eba80424c67df193f3e12f95a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Malak?= Date: Fri, 25 Mar 2022 14:28:40 +0100 Subject: [PATCH 22/33] Added option to set secondary search provider --- CHANGELOG.md | 1 + client/src/components/SearchBar/SearchBar.tsx | 27 ++++++++------ .../GeneralSettings/GeneralSettings.tsx | 30 ++++++++++++++- client/src/interfaces/Config.ts | 1 + client/src/interfaces/Forms.ts | 1 + client/src/interfaces/SearchResult.ts | 5 ++- client/src/utility/searchParser.ts | 37 +++++++++++++------ .../utility/templateObjects/configTemplate.ts | 1 + utils/init/initialConfig.json | 1 + 9 files changed, 79 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f8b571d..f4521eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ ### v2.3.0 (TBA) - Added custom theme editor ([#246](https://github.com/pawelmalak/flame/issues/246)) +- Added option to set secondary search provider ([#295](https://github.com/pawelmalak/flame/issues/295)) - Fixed bug where pressing Enter with empty search bar would redirect to search results ([#325](https://github.com/pawelmalak/flame/issues/325)) - Fixed bug where user could create empty app or bookmark which was causing page to go blank ([#332](https://github.com/pawelmalak/flame/issues/332)) - Added new theme: Mint diff --git a/client/src/components/SearchBar/SearchBar.tsx b/client/src/components/SearchBar/SearchBar.tsx index 2ec3420..f0b78a5 100644 --- a/client/src/components/SearchBar/SearchBar.tsx +++ b/client/src/components/SearchBar/SearchBar.tsx @@ -64,16 +64,22 @@ export const SearchBar = (props: Props): JSX.Element => { }; const searchHandler = (e: KeyboardEvent) => { - const { isLocal, search, query, isURL, sameTab, rawQuery } = searchParser( - inputRef.current.value - ); + const { + isLocal, + encodedURL, + primarySearch, + secondarySearch, + isURL, + sameTab, + rawQuery, + } = searchParser(inputRef.current.value); if (isLocal) { - setLocalSearch(search); + setLocalSearch(encodedURL); } if (e.code === 'Enter' || e.code === 'NumpadEnter') { - if (!query.prefix) { + if (!primarySearch.prefix) { // Prefix not found -> emit notification createNotification({ title: 'Error', @@ -91,21 +97,20 @@ export const SearchBar = (props: Props): JSX.Element => { redirectUrl(bookmarkSearchResult[0].bookmarks[0].url, sameTab); } else { // no local results -> search the internet with the default search provider if query is not empty - if (!/^ *$/.test(rawQuery)) { - let template = query.template; + let template = primarySearch.template; - if (query.prefix === 'l') { - template = 'https://duckduckgo.com/?q='; + if (primarySearch.prefix === 'l') { + template = secondarySearch.template; } - const url = `${template}${search}`; + const url = `${template}${encodedURL}`; redirectUrl(url, sameTab); } } } else { // Valid query -> redirect to search results - const url = `${query.template}${search}`; + const url = `${primarySearch.template}${encodedURL}`; redirectUrl(url, sameTab); } } else if (e.code === 'Escape') { diff --git a/client/src/components/Settings/GeneralSettings/GeneralSettings.tsx b/client/src/components/Settings/GeneralSettings/GeneralSettings.tsx index d88208e..61bc6c2 100644 --- a/client/src/components/Settings/GeneralSettings/GeneralSettings.tsx +++ b/client/src/components/Settings/GeneralSettings/GeneralSettings.tsx @@ -167,7 +167,7 @@ export const GeneralSettings = (): JSX.Element => { {/* === SEARCH OPTIONS === */} - + + {formData.defaultSearchProvider === 'l' && ( + + + + + Will be used when "Local search" is primary search provider and + there are not any local results + + + )} +