Przeglądaj źródła

Reworked OtherSettings to work with global config state. Fixed bug with certain settings not being synchronized

unknown 4 lat temu
rodzic
commit
5e7cb72b82

+ 0 - 4
client/src/App.tsx

@@ -18,10 +18,6 @@ if (localStorage.theme) {
   store.dispatch<any>(setTheme(localStorage.theme));
 }
 
-if (localStorage.customTitle) {
-  document.title = localStorage.customTitle;
-}
-
 const App = (): JSX.Element => {
   return (
     <Provider store={store}>

+ 2 - 2
client/src/components/Apps/AppForm/AppForm.tsx

@@ -100,10 +100,10 @@ const AppForm = (props: ComponentProps): JSX.Element => {
         />
         <span>
           <a
-            href='https://github.com/pawelmalak/flame#supported-URL-formats-for-applications-and-bookmarks'
+            href='https://github.com/pawelmalak/flame#supported-url-formats-for-applications-and-bookmarks'
             target='_blank'
             rel='noreferrer'
-            >
+          >
             {' '}Check supported URL formats
           </a>
         </span>

+ 2 - 2
client/src/components/Bookmarks/BookmarkForm/BookmarkForm.tsx

@@ -186,10 +186,10 @@ const BookmarkForm = (props: ComponentProps): JSX.Element => {
               />
               <span>
                 <a
-                  href='https://github.com/pawelmalak/flame#supported-URL-formats-for-applications-and-bookmarks'
+                  href='https://github.com/pawelmalak/flame#supported-url-formats-for-applications-and-bookmarks'
                   target='_blank'
                   rel='noreferrer'
-                  >
+                >
                   {' '}Check supported URL formats
                 </a>
               </span>

+ 28 - 15
client/src/components/Home/Home.tsx

@@ -27,6 +27,9 @@ import WeatherWidget from '../Widgets/WeatherWidget/WeatherWidget';
 import { greeter } from './functions/greeter';
 import { dateTime } from './functions/dateTime';
 
+// Utils
+import { searchConfig } from '../../utility';
+
 interface ComponentProps {
   getApps: Function;
   getCategories: Function;
@@ -67,26 +70,36 @@ const Home = (props: ComponentProps): JSX.Element => {
 
   // Refresh greeter and time
   useEffect(() => {
-    const interval = setInterval(() => {
-      setHeader({
-        dateTime: dateTime(),
-        greeting: greeter()
-      })
-    }, 1000);
+    let interval: any;
+
+    // Start interval only when hideHeader is false
+    if (searchConfig('hideHeader', 0) !== 1) {
+      interval = setInterval(() => {
+        setHeader({
+          dateTime: dateTime(),
+          greeting: greeter()
+        })
+      }, 1000);
+    }
 
     return () => clearInterval(interval);
   }, [])
-
+  
   return (
     <Container>
-      <header className={classes.Header}>
-        <p>{header.dateTime}</p>
-        <Link to='/settings' className={classes.SettingsLink}>Go to Settings</Link>
-        <span className={classes.HeaderMain}>
-          <h1>{header.greeting}</h1>
-          <WeatherWidget />
-        </span>
-      </header>
+      {searchConfig('hideHeader', 0) !== 1
+        ? (
+          <header className={classes.Header}>
+            <p>{header.dateTime}</p>
+            <Link to='/settings' className={classes.SettingsLink}>Go to Settings</Link>
+            <span className={classes.HeaderMain}>
+              <h1>{header.greeting}</h1>
+              <WeatherWidget />
+            </span>
+          </header>
+          )
+        : <div></div>
+      }
       
       <SectionHeadline title='Applications' link='/applications' />
       {appsLoading

+ 50 - 45
client/src/components/Settings/OtherSettings/OtherSettings.tsx

@@ -1,69 +1,56 @@
 import { useState, useEffect, ChangeEvent, FormEvent } from 'react';
-import axios from 'axios';
+
+// Redux
 import { connect } from 'react-redux';
+import { createNotification, updateConfig } from '../../../store/actions';
+
+// Typescript
+import { GlobalState, NewNotification, SettingsForm } from '../../../interfaces';
 
+// UI
 import InputGroup from '../../UI/Forms/InputGroup/InputGroup';
 import Button from '../../UI/Buttons/Button/Button';
-import { createNotification } from '../../../store/actions';
-import { ApiResponse, Config, NewNotification } from '../../../interfaces';
 
-interface FormState {
-  customTitle: string;
-  pinAppsByDefault: number;
-  pinCategoriesByDefault: number;
-}
+// Utils
+import { searchConfig } from '../../../utility';
 
 interface ComponentProps {
   createNotification: (notification: NewNotification) => void;
+  updateConfig: (formData: SettingsForm) => void;
+  loading: boolean;
 }
 
 const OtherSettings = (props: ComponentProps): JSX.Element => {
-  const [formData, setFormData] = useState<FormState>({
+  // Initial state
+  const [formData, setFormData] = useState<SettingsForm>({
     customTitle: document.title,
-    pinAppsByDefault: 0,
-    pinCategoriesByDefault: 0
+    pinAppsByDefault: 1,
+    pinCategoriesByDefault: 1,
+    hideHeader: 0
   })
 
-  // get initial config
+  // Get config
   useEffect(() => {
-    axios.get<ApiResponse<Config[]>>('/api/config?keys=customTitle,pinAppsByDefault,pinCategoriesByDefault')
-      .then(data => {
-        let tmpFormData = { ...formData };
-
-        data.data.data.forEach((config: Config) => {
-          let value: string | number = config.value;
-          if (config.valueType === 'number') {
-            value = parseFloat(value);
-          }
-
-          tmpFormData = {
-            ...tmpFormData,
-            [config.key]: value
-          }
-        })
-
-        setFormData(tmpFormData);
-      })
-      .catch(err => console.log(err));
-  }, [])
-
-  const formSubmitHandler = (e: FormEvent) => {
+    setFormData({
+      customTitle: searchConfig('customTitle', 'Flame'),
+      pinAppsByDefault: searchConfig('pinAppsByDefault', 1),
+      pinCategoriesByDefault: searchConfig('pinCategoriesByDefault', 1),
+      hideHeader: searchConfig('hideHeader', 0)
+    })
+  }, [props.loading]);
+
+  // Form handler
+  const formSubmitHandler = async (e: FormEvent) => {
     e.preventDefault();
 
-    axios.put<ApiResponse<{}>>('/api/config', formData)
-      .then(() => {
-        props.createNotification({
-          title: 'Success',
-          message: 'Settings updated'
-        })
-      })
-      .catch((err) => console.log(err));
+    // Save settings
+    await props.updateConfig(formData);
 
     // update local page title
-    localStorage.setItem('customTitle', formData.customTitle);
     document.title = formData.customTitle;
   }
 
+  // Input handler
   const inputChangeHandler = (e: ChangeEvent<HTMLInputElement | HTMLSelectElement>, isNumber?: boolean) => {
     let value: string | number = e.target.value;
 
@@ -80,7 +67,7 @@ const OtherSettings = (props: ComponentProps): JSX.Element => {
   return (
     <form onSubmit={(e) => formSubmitHandler(e)}>
       <InputGroup>
-        <label htmlFor='customTitle'>Custom Page Title</label>
+        <label htmlFor='customTitle'>Custom page title</label>
         <input
           type='text'
           id='customTitle'
@@ -114,9 +101,27 @@ const OtherSettings = (props: ComponentProps): JSX.Element => {
           <option value={0}>False</option>
         </select>
       </InputGroup>
+      <InputGroup>
+        <label htmlFor='hideHeader'>Hide greeting and date</label>
+        <select
+          id='hideHeader'
+          name='hideHeader'
+          value={formData.hideHeader}
+          onChange={(e) => inputChangeHandler(e, true)}
+        >
+          <option value={1}>True</option>
+          <option value={0}>False</option>
+        </select>
+      </InputGroup>
     <Button>Save changes</Button>
     </form>
   )
 }
 
-export default connect(null, { createNotification })(OtherSettings);
+const mapStateToProps = (state: GlobalState) => {
+  return {
+    loading: state.config.loading
+  }
+}
+
+export default connect(mapStateToProps, { createNotification, updateConfig })(OtherSettings);

+ 22 - 18
client/src/components/Settings/WeatherSettings/WeatherSettings.tsx

@@ -22,6 +22,7 @@ interface ComponentProps {
 }
 
 const WeatherSettings = (props: ComponentProps): JSX.Element => {
+  // Initial state
   const [formData, setFormData] = useState<WeatherForm>({
     WEATHER_API_KEY: '',
     lat: 0,
@@ -29,19 +30,7 @@ const WeatherSettings = (props: ComponentProps): JSX.Element => {
     isCelsius: 1
   })
 
-  const inputChangeHandler = (e: ChangeEvent<HTMLInputElement | HTMLSelectElement>, isNumber?: boolean) => {
-    let value: string | number = e.target.value;
-
-    if (isNumber) {
-      value = parseFloat(value);
-    }
-
-    setFormData({
-      ...formData,
-      [e.target.name]: value
-    })
-  }
-
+  // Get config
   useEffect(() => {
     setFormData({
       WEATHER_API_KEY: searchConfig('WEATHER_API_KEY', ''),
@@ -51,6 +40,7 @@ const WeatherSettings = (props: ComponentProps): JSX.Element => {
     })
   }, [props.loading]);
 
+  // Form handler
   const formSubmitHandler = async (e: FormEvent) => {
     e.preventDefault();
 
@@ -58,7 +48,7 @@ const WeatherSettings = (props: ComponentProps): JSX.Element => {
     if ((formData.lat || formData.long) && !formData.WEATHER_API_KEY) {
       props.createNotification({
         title: 'Warning',
-        message: 'API Key is missing. Weather Module will NOT work'
+        message: 'API key is missing. Weather Module will NOT work'
       })
     }
 
@@ -81,10 +71,24 @@ const WeatherSettings = (props: ComponentProps): JSX.Element => {
       });
   }
 
+  // Input handler
+  const inputChangeHandler = (e: ChangeEvent<HTMLInputElement | HTMLSelectElement>, isNumber?: boolean) => {
+    let value: string | number = e.target.value;
+
+    if (isNumber) {
+      value = parseFloat(value);
+    }
+
+    setFormData({
+      ...formData,
+      [e.target.name]: value
+    })
+  }
+
   return (
     <form onSubmit={(e) => formSubmitHandler(e)}>
       <InputGroup>
-        <label htmlFor='WEATHER_API_KEY'>API Key</label>
+        <label htmlFor='WEATHER_API_KEY'>API key</label>
         <input
           type='text'
           id='WEATHER_API_KEY'
@@ -104,7 +108,7 @@ const WeatherSettings = (props: ComponentProps): JSX.Element => {
         </span>
       </InputGroup>
       <InputGroup>
-        <label htmlFor='lat'>Location Latitude</label>
+        <label htmlFor='lat'>Location latitude</label>
         <input
           type='number'
           id='lat'
@@ -123,7 +127,7 @@ const WeatherSettings = (props: ComponentProps): JSX.Element => {
         </span>
       </InputGroup>
       <InputGroup>
-        <label htmlFor='long'>Location Longitude</label>
+        <label htmlFor='long'>Location longitude</label>
         <input
           type='number'
           id='long'
@@ -134,7 +138,7 @@ const WeatherSettings = (props: ComponentProps): JSX.Element => {
         />
       </InputGroup>
       <InputGroup>
-        <label htmlFor='isCelsius'>Temperature Unit</label>
+        <label htmlFor='isCelsius'>Temperature unit</label>
         <select
           id='isCelsius'
           name='isCelsius'

+ 1 - 1
client/src/components/Widgets/WeatherWidget/WeatherWidget.tsx

@@ -68,7 +68,7 @@ const WeatherWidget = (props: ComponentProps): JSX.Element => {
 
   return (
     <div className={classes.WeatherWidget}>
-      {isLoading || props.configLoading || searchConfig('WEATHER_API_KEY', '') && 
+      {(isLoading || props.configLoading || searchConfig('WEATHER_API_KEY', '')) && 
          (weather.id > 0 && 
             (<Fragment>
               <div className={classes.WeatherIcon}>

+ 7 - 0
client/src/interfaces/Forms.ts

@@ -3,4 +3,11 @@ export interface WeatherForm {
   lat: number;
   long: number;
   isCelsius: number;
+}
+
+export interface SettingsForm {
+  customTitle: string;
+  pinAppsByDefault: number;
+  pinCategoriesByDefault: number;
+  hideHeader: number;
 }

+ 7 - 3
client/src/store/actions/config.ts

@@ -1,8 +1,9 @@
 import axios from 'axios';
 import { Dispatch } from 'redux';
 import { ActionTypes } from './actionTypes';
-import { Config, ApiResponse, WeatherForm } from '../../interfaces';
+import { Config, ApiResponse } from '../../interfaces';
 import { CreateNotificationAction } from './notification';
+import { searchConfig } from '../../utility';
 
 export interface GetConfigAction {
   type: ActionTypes.getConfig;
@@ -12,11 +13,14 @@ export interface GetConfigAction {
 export const getConfig = () => async (dispatch: Dispatch) => {
   try {
     const res = await axios.get<ApiResponse<Config[]>>('/api/config');
-
+   
     dispatch<GetConfigAction>({
       type: ActionTypes.getConfig,
       payload: res.data.data
     })
+
+    // Set custom page title if set
+    document.title = searchConfig('customTitle', 'Flame');
   } catch (err) {
     console.log(err)
   }
@@ -27,7 +31,7 @@ export interface UpdateConfigAction {
   payload: Config[];
 }
 
-export const updateConfig = (formData: WeatherForm) => async (dispatch: Dispatch) => {
+export const updateConfig = (formData: any) => async (dispatch: Dispatch) => {
   try {
     const res = await axios.put<ApiResponse<Config[]>>('/api/config', formData);
     dispatch<CreateNotificationAction>({

+ 1 - 1
client/src/utility/searchConfig.ts

@@ -5,7 +5,7 @@ import { store } from '../store/store';
  * @param key Config pair key to search
  * @param _default Value to return if key is not found
  */
-export const searchConfig = (key: string, _default: any)=> {
+export const searchConfig = (key: string, _default: any) => {
   const state = store.getState();
 
   const pair = state.config.config.find(p => p.key === key);

+ 4 - 0
utils/initialConfig.json

@@ -27,6 +27,10 @@
     {
       "key": "pinCategoriesByDefault",
       "value": true
+    },
+    {
+      "key": "hideHeader",
+      "value": false
     }
   ]
 }