ソースを参照

Merge pull request #9 from pawelmalak/qol-improvements

Added favicon and changed page title. Changed message when there are …
pawelmalak 4 年 前
コミット
cf44f45fde

BIN
client/public/favicon.ico


+ 3 - 24
client/public/index.html

@@ -10,36 +10,15 @@
       content="Web site created using create-react-app"
       content="Web site created using create-react-app"
     />
     />
     <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
     <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
-    <!--
-      manifest.json provides metadata used when your web app is installed on a
-      user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-    -->
     <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
     <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
-    <!--
-      Notice the use of %PUBLIC_URL% in the tags above.
-      It will be replaced with the URL of the `public` folder during the build.
-      Only files inside the `public` folder can be referenced from the HTML.
-
-      Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
-      work correctly both with client-side routing and a non-root public URL.
-      Learn how to configure a non-root public URL by running `npm run build`.
-    -->
     <link rel="preconnect" href="https://fonts.gstatic.com">
     <link rel="preconnect" href="https://fonts.gstatic.com">
     <link href="https://fonts.googleapis.com/css?family=Roboto:400,500,700,900" rel="stylesheet">
     <link href="https://fonts.googleapis.com/css?family=Roboto:400,500,700,900" rel="stylesheet">
-    <title>React App</title>
+    <title>Flame</title>
   </head>
   </head>
   <body>
   <body>
+
     <noscript>You need to enable JavaScript to run this app.</noscript>
     <noscript>You need to enable JavaScript to run this app.</noscript>
     <div id="root"></div>
     <div id="root"></div>
-    <!--
-      This HTML file is a template.
-      If you open it directly in the browser, you will see an empty page.
-
-      You can add webfonts, meta tags, or analytics to this file.
-      The build step will place the bundled scripts into the <body> tag.
-
-      To begin the development, run `npm start` or `yarn start`.
-      To create a production bundle, use `npm run build` or `yarn build`.
-    -->
+    
   </body>
   </body>
 </html>
 </html>

BIN
client/public/logo192.png


BIN
client/public/logo512.png


+ 0 - 25
client/public/manifest.json

@@ -1,25 +0,0 @@
-{
-  "short_name": "React App",
-  "name": "Create React App Sample",
-  "icons": [
-    {
-      "src": "favicon.ico",
-      "sizes": "64x64 32x32 24x24 16x16",
-      "type": "image/x-icon"
-    },
-    {
-      "src": "logo192.png",
-      "type": "image/png",
-      "sizes": "192x192"
-    },
-    {
-      "src": "logo512.png",
-      "type": "image/png",
-      "sizes": "512x512"
-    }
-  ],
-  "start_url": ".",
-  "display": "standalone",
-  "theme_color": "#000000",
-  "background_color": "#ffffff"
-}

+ 4 - 0
client/src/App.tsx

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

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

@@ -98,7 +98,7 @@ const AppForm = (props: ComponentProps): JSX.Element => {
           value={formData.url}
           value={formData.url}
           onChange={(e) => inputChangeHandler(e)}
           onChange={(e) => inputChangeHandler(e)}
         />
         />
-        <span>Use URL without protocol</span>
+        <span>Only urls without http[s]:// are supported</span>
       </InputGroup>
       </InputGroup>
       <InputGroup>
       <InputGroup>
         <label htmlFor='icon'>App Icon</label>
         <label htmlFor='icon'>App Icon</label>

+ 10 - 3
client/src/components/Apps/AppGrid/AppGrid.tsx

@@ -6,6 +6,7 @@ import AppCard from '../AppCard/AppCard';
 
 
 interface ComponentProps {
 interface ComponentProps {
   apps: App[];
   apps: App[];
+  totalApps?: number;
 }
 }
 
 
 const AppGrid = (props: ComponentProps): JSX.Element => {
 const AppGrid = (props: ComponentProps): JSX.Element => {
@@ -23,9 +24,15 @@ const AppGrid = (props: ComponentProps): JSX.Element => {
       </div>
       </div>
     )
     )
   } else {
   } else {
-    apps = (
-      <p className={classes.AppsMessage}>You don't have any applications. You can add a new one from <Link to='/applications'>/application</Link> menu</p>
-    );
+    if (props.totalApps) {
+      apps = (
+        <p className={classes.AppsMessage}>There are no pinned applications. You can pin them from the <Link to='/applications'>/applications</Link> menu</p>
+      );
+    } else {
+      apps = (
+        <p className={classes.AppsMessage}>You don't have any applications. You can add a new one from <Link to='/applications'>/applications</Link> menu</p>
+      );
+    }
   }
   }
 
 
   return apps;
   return apps;

+ 1 - 0
client/src/components/Bookmarks/BookmarkForm/BookmarkForm.tsx

@@ -177,6 +177,7 @@ const BookmarkForm = (props: ComponentProps): JSX.Element => {
                 value={formData.url}
                 value={formData.url}
                 onChange={(e) => inputChangeHandler(e)}
                 onChange={(e) => inputChangeHandler(e)}
               />
               />
+              <span>Only urls without http[s]:// are supported</span>
             </InputGroup>
             </InputGroup>
             <InputGroup>
             <InputGroup>
               <label htmlFor='categoryId'>Bookmark Category</label>
               <label htmlFor='categoryId'>Bookmark Category</label>

+ 10 - 3
client/src/components/Bookmarks/BookmarkGrid/BookmarkGrid.tsx

@@ -8,6 +8,7 @@ import BookmarkCard from '../BookmarkCard/BookmarkCard';
 
 
 interface ComponentProps {
 interface ComponentProps {
   categories: Category[];
   categories: Category[];
+  totalCategories?: number;
 }
 }
 
 
 const BookmarkGrid = (props: ComponentProps): JSX.Element => {
 const BookmarkGrid = (props: ComponentProps): JSX.Element => {
@@ -20,9 +21,15 @@ const BookmarkGrid = (props: ComponentProps): JSX.Element => {
       </div>
       </div>
     );
     );
   } else {
   } else {
-    bookmarks = (
-      <p className={classes.BookmarksMessage}>You don't have any bookmarks. You can add a new one from <Link to='/bookmarks'>/bookmarks</Link> menu</p>
-    );
+    if (props.totalCategories) {
+      bookmarks = (
+        <p className={classes.BookmarksMessage}>There are no pinned categories. You can pin them from the <Link to='/bookmarks'>/bookmarks</Link> menu</p>
+      );
+    } else {
+      bookmarks = (
+        <p className={classes.BookmarksMessage}>You don't have any bookmarks. You can add a new one from <Link to='/bookmarks'>/bookmarks</Link> menu</p>
+      );
+    }
   }
   }
 
 
   return bookmarks;
   return bookmarks;

+ 8 - 2
client/src/components/Home/Home.tsx

@@ -81,7 +81,10 @@ const Home = (props: ComponentProps): JSX.Element => {
       <SectionHeadline title='Applications' link='/applications' />
       <SectionHeadline title='Applications' link='/applications' />
       {props.appsLoading
       {props.appsLoading
         ? <Spinner />
         ? <Spinner />
-        : <AppGrid apps={props.apps.filter((app: App) => app.isPinned)} />
+        : <AppGrid
+          apps={props.apps.filter((app: App) => app.isPinned)}
+          totalApps={props.apps.length}
+        />
       }
       }
 
 
       <div className={classes.HomeSpace}></div>
       <div className={classes.HomeSpace}></div>
@@ -89,7 +92,10 @@ const Home = (props: ComponentProps): JSX.Element => {
       <SectionHeadline title='Bookmarks' link='/bookmarks' />
       <SectionHeadline title='Bookmarks' link='/bookmarks' />
       {props.categoriesLoading
       {props.categoriesLoading
         ? <Spinner />
         ? <Spinner />
-        : <BookmarkGrid categories={props.categories.filter((category: Category) => category.isPinned)} />
+        : <BookmarkGrid
+            categories={props.categories.filter((category: Category) => category.isPinned)}
+            totalCategories={props.categories.length}
+        />
       }
       }
 
 
       <Link to='/settings' className={classes.SettingsButton}>
       <Link to='/settings' className={classes.SettingsButton}>

+ 88 - 0
client/src/components/Settings/OtherSettings/OtherSettings.tsx

@@ -0,0 +1,88 @@
+import { useState, useEffect, ChangeEvent, FormEvent } from 'react';
+import axios from 'axios';
+import { connect } from 'react-redux';
+
+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;
+}
+
+interface ComponentProps {
+  createNotification: (notification: NewNotification) => void;
+}
+
+const OtherSettings = (props: ComponentProps): JSX.Element => {
+  const [formData, setFormData] = useState<FormState>({
+    customTitle: document.title
+  })
+
+  // get initial config
+  useEffect(() => {
+    axios.get<ApiResponse<Config[]>>('/api/config?keys=customTitle')
+      .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) => {
+    e.preventDefault();
+
+    axios.put<ApiResponse<{}>>('/api/config', formData)
+      .then(() => {
+        props.createNotification({
+          title: 'Success',
+          message: 'Settings updated'
+        })
+      })
+      .catch((err) => console.log(err));
+
+    // update local page title
+    localStorage.setItem('customTitle', formData.customTitle);
+    document.title = formData.customTitle;
+  }
+
+  const inputChangeHandler = (e: ChangeEvent<HTMLInputElement>) => {
+    setFormData({
+      ...formData,
+      [e.target.name]: e.target.value
+    })
+  }
+
+  return (
+    <form onSubmit={(e) => formSubmitHandler(e)}>
+      <InputGroup>
+        <label htmlFor='customTitle'>Custom Page Title</label>
+        <input
+          type='text'
+          id='customTitle'
+          name='customTitle'
+          placeholder='Flame'
+          value={formData.customTitle}
+          onChange={(e) => inputChangeHandler(e)}
+        />
+      </InputGroup>
+    <Button>Save changes</Button>
+    </form>
+  )
+}
+
+export default connect(null, { createNotification })(OtherSettings);

+ 9 - 0
client/src/components/Settings/Settings.tsx

@@ -6,6 +6,7 @@ import { Container } from '../UI/Layout/Layout';
 import Headline from '../UI/Headlines/Headline/Headline';
 import Headline from '../UI/Headlines/Headline/Headline';
 import Themer from '../Themer/Themer';
 import Themer from '../Themer/Themer';
 import WeatherSettings from './WeatherSettings/WeatherSettings';
 import WeatherSettings from './WeatherSettings/WeatherSettings';
+import OtherSettings from './OtherSettings/OtherSettings';
 
 
 const Settings = (): JSX.Element => {
 const Settings = (): JSX.Element => {
   return (
   return (
@@ -30,11 +31,19 @@ const Settings = (): JSX.Element => {
             to='/settings/weather'>
             to='/settings/weather'>
             Weather
             Weather
           </NavLink>
           </NavLink>
+          <NavLink
+            className={classes.SettingsNavLink}
+            activeClassName={classes.SettingsNavLinkActive}
+            exact
+            to='/settings/other'>
+            Other
+          </NavLink>
         </nav>
         </nav>
         <section className={classes.SettingsContent}>
         <section className={classes.SettingsContent}>
           <Switch>
           <Switch>
             <Route exact path='/settings' component={Themer} />
             <Route exact path='/settings' component={Themer} />
             <Route path='/settings/weather' component={WeatherSettings} />
             <Route path='/settings/weather' component={WeatherSettings} />
+            <Route path='/settings/other' component={OtherSettings} />
           </Switch>
           </Switch>
         </section>
         </section>
       </div>
       </div>

+ 2 - 2
utils/initConfig.js

@@ -3,8 +3,8 @@ const Config = require('../models/Config');
 
 
 const initConfig = async () => {
 const initConfig = async () => {
   // Config keys
   // Config keys
-  const keys = ['WEATHER_API_KEY', 'lat', 'long', 'isCelsius'];
-  const values = ['', 0, 0, true];
+  const keys = ['WEATHER_API_KEY', 'lat', 'long', 'isCelsius', 'customTitle'];
+  const values = ['', 0, 0, true, 'Flame'];
 
 
   // Get config values
   // Get config values
   const configPairs = await Config.findAll({
   const configPairs = await Config.findAll({