Browse Source

Added auto-refresh for greeting and date. Fixed multiple React warnings

unknown 4 years ago
parent
commit
f137498e7e

+ 1 - 1
README.md

@@ -62,7 +62,7 @@ docker run -p 5005:5005 -v <host_dir>:/app/data flame
 ![Homescreen screenshot](./github/_themes.png)
 
 ## Usage
-### Supported links for applications and bookmarks
+### Supported URL formats for applications and bookmarks
 #### Rules
 - URL starts with `http://`
   - Format: `http://www.domain.com`, `http://domain.com`

+ 0 - 0
client/src/App.module.css


+ 0 - 2
client/src/App.tsx

@@ -5,8 +5,6 @@ import { getConfig, setTheme } from './store/actions';
 import { store } from './store/store';
 import { Provider } from 'react-redux';
 
-import classes from './App.module.css';
-
 import Home from './components/Home/Home';
 import Apps from './components/Apps/Apps';
 import Settings from './components/Settings/Settings';

+ 6 - 1
client/src/components/Apps/AppCard/AppCard.tsx

@@ -13,7 +13,12 @@ const AppCard = (props: ComponentProps): JSX.Element => {
   const [displayUrl, redirectUrl] = urlParser(props.app.url);
 
   return (
-    <a href={redirectUrl} target='_blank' className={classes.AppCard}>
+    <a
+      href={redirectUrl}
+      target='_blank'
+      rel='noreferrer'
+      className={classes.AppCard}
+    >
       <div className={classes.AppCardIcon}>
         <Icon icon={iconParser(props.app.icon)} />
       </div>

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

@@ -98,7 +98,15 @@ const AppForm = (props: ComponentProps): JSX.Element => {
           value={formData.url}
           onChange={(e) => inputChangeHandler(e)}
         />
-        <span>Only urls without http[s]:// are supported</span>
+        <span>
+          <a
+            href='https://github.com/pawelmalak/flame#supported-URL-formats-for-applications-and-bookmarks'
+            target='_blank'
+            rel='noreferrer'
+            >
+            {' '}Check supported URL formats
+          </a>
+        </span>
       </InputGroup>
       <InputGroup>
         <label htmlFor='icon'>App Icon</label>

+ 12 - 6
client/src/components/Apps/Apps.tsx

@@ -1,4 +1,4 @@
-import { Fragment, useEffect, useState } from 'react';
+import { useEffect, useState } from 'react';
 import { Link } from 'react-router-dom';
 
 // Redux
@@ -30,6 +30,12 @@ interface ComponentProps {
 }
 
 const Apps = (props: ComponentProps): JSX.Element => {
+  const {
+    getApps,
+    apps,
+    loading
+  } = props;
+
   const [modalIsOpen, setModalIsOpen] = useState(false);
   const [isInEdit, setIsInEdit] = useState(false);
   const [isInUpdate, setIsInUpdate] = useState(false);
@@ -44,10 +50,10 @@ const Apps = (props: ComponentProps): JSX.Element => {
   })
 
   useEffect(() => {
-    if (props.apps.length === 0) {
-      props.getApps();
+    if (apps.length === 0) {
+      getApps();
     }
-  }, [props.getApps]);
+  }, [getApps, apps]);
 
   const toggleModal = (): void => {
     setModalIsOpen(!modalIsOpen);
@@ -93,10 +99,10 @@ const Apps = (props: ComponentProps): JSX.Element => {
       </div>
 
       <div className={classes.Apps}>
-        {props.loading
+        {loading
           ? <Spinner />
           : (!isInEdit
-              ? <AppGrid apps={props.apps} />
+              ? <AppGrid apps={apps} />
               : <AppTable updateAppHandler={toggleUpdate} />)
         }
       </div>

+ 2 - 1
client/src/components/Bookmarks/BookmarkCard/BookmarkCard.tsx

@@ -14,12 +14,13 @@ const BookmarkCard = (props: ComponentProps): JSX.Element => {
       <h3>{props.category.name}</h3>
       <div className={classes.Bookmarks}>
         {props.category.bookmarks.map((bookmark: Bookmark) => {
-          const [displayUrl, redirectUrl] = urlParser(bookmark.url);
+          const redirectUrl = urlParser(bookmark.url)[1];
 
           return (
             <a
               href={redirectUrl}
               target='_blank'
+              rel='noreferrer'
               key={`bookmark-${bookmark.id}`}>
               {bookmark.icon && (
                 <div className={classes.BookmarkIcon}>

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

@@ -184,7 +184,15 @@ const BookmarkForm = (props: ComponentProps): JSX.Element => {
                 value={formData.url}
                 onChange={(e) => inputChangeHandler(e)}
               />
-              <span>Only urls without http[s]:// are supported</span>
+              <span>
+                <a
+                  href='https://github.com/pawelmalak/flame#supported-URL-formats-for-applications-and-bookmarks'
+                  target='_blank'
+                  rel='noreferrer'
+                  >
+                  {' '}Check supported URL formats
+                </a>
+              </span>
             </InputGroup>
             <InputGroup>
               <label htmlFor='categoryId'>Bookmark Category</label>

+ 1 - 1
client/src/components/Bookmarks/BookmarkGrid/BookmarkGrid.tsx

@@ -2,7 +2,7 @@ import { Link } from 'react-router-dom';
 
 import classes from './BookmarkGrid.module.css';
 
-import { Bookmark, Category } from '../../../interfaces';
+import { Category } from '../../../interfaces';
 
 import BookmarkCard from '../BookmarkCard/BookmarkCard';
 

+ 12 - 6
client/src/components/Bookmarks/Bookmarks.tsx

@@ -28,6 +28,12 @@ export enum ContentType {
 }
 
 const Bookmarks = (props: ComponentProps): JSX.Element => {
+  const {
+    getCategories,
+    categories,
+    loading
+  } = props;
+
   const [modalIsOpen, setModalIsOpen] = useState(false);
   const [formContentType, setFormContentType] = useState(ContentType.category);
   const [isInEdit, setIsInEdit] = useState(false);
@@ -52,10 +58,10 @@ const Bookmarks = (props: ComponentProps): JSX.Element => {
   })
 
   useEffect(() => {
-    if (props.categories.length === 0) {
-      props.getCategories();
+    if (categories.length === 0) {
+      getCategories();
     }
-  }, [props.getCategories])
+  }, [getCategories, categories])
 
   const toggleModal = (): void => {
     setModalIsOpen(!modalIsOpen);
@@ -132,13 +138,13 @@ const Bookmarks = (props: ComponentProps): JSX.Element => {
         />
       </div>
 
-      {props.loading
+      {loading
         ? <Spinner />
         : (!isInEdit
-          ? <BookmarkGrid categories={props.categories} />
+          ? <BookmarkGrid categories={categories} />
           : <BookmarkTable
               contentType={tableContentType}
-              categories={props.categories}
+              categories={categories}
               updateHandler={goToUpdateMode}
             />
           )

+ 45 - 35
client/src/components/Home/Home.tsx

@@ -1,4 +1,4 @@
-import { useEffect } from 'react';
+import { useState, useEffect } from 'react';
 import { Link } from 'react-router-dom';
 
 // Redux
@@ -23,6 +23,10 @@ import AppGrid from '../Apps/AppGrid/AppGrid';
 import BookmarkGrid from '../Bookmarks/BookmarkGrid/BookmarkGrid';
 import WeatherWidget from '../Widgets/WeatherWidget/WeatherWidget';
 
+// Functions
+import { greeter } from './functions/greeter';
+import { dateTime } from './functions/dateTime';
+
 interface ComponentProps {
   getApps: Function;
   getCategories: Function;
@@ -33,68 +37,74 @@ interface ComponentProps {
 }
 
 const Home = (props: ComponentProps): JSX.Element => {
+  const {
+    getApps,
+    apps,
+    appsLoading,
+    getCategories,
+    categories,
+    categoriesLoading
+  } = props;
+
+  const [header, setHeader] = useState({
+    dateTime: dateTime(),
+    greeting: greeter()
+  })
+
+  // Load applications
   useEffect(() => {
-    if (props.apps.length === 0) {
-      props.getApps();
+    if (apps.length === 0) {
+      getApps();
     }
-  }, [props.getApps]);
+  }, [getApps, apps]);
 
+  // Load bookmark categories
   useEffect(() => {
-    if (props.categories.length === 0) {
-      props.getCategories();
+    if (categories.length === 0) {
+      getCategories();
     }
-  }, [props.getCategories]);
-
-  const dateAndTime = (): string => {
-    const days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
-    const months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
-
-    const now = new Date();
-
-    return `${days[now.getDay()]}, ${now.getDate()} ${months[now.getMonth()]} ${now.getFullYear()}`;
-  }
-
-  const greeter = (): string => {
-    const now = new Date().getHours();
-    let msg: string;
+  }, [getCategories, categories]);
 
-    if (now >= 18) msg = 'Good evening!';
-    else if (now >= 12) msg = 'Good afternoon!';
-    else if (now >= 6) msg = 'Good morning!';
-    else if (now >= 0) msg = 'Good night!';
-    else msg = 'Hello!';
+  // Refresh greeter and time
+  useEffect(() => {
+    const interval = setInterval(() => {
+      setHeader({
+        dateTime: dateTime(),
+        greeting: greeter()
+      })
+    }, 1000);
 
-    return msg;
-  }
+    return () => clearInterval(interval);
+  }, [])
 
   return (
     <Container>
       <header className={classes.Header}>
-        <p>{dateAndTime()}</p>
+        <p>{header.dateTime}</p>
         <Link to='/settings' className={classes.SettingsLink}>Go to Settings</Link>
         <span className={classes.HeaderMain}>
-          <h1>{greeter()}</h1>
+          <h1>{header.greeting}</h1>
           <WeatherWidget />
         </span>
       </header>
       
       <SectionHeadline title='Applications' link='/applications' />
-      {props.appsLoading
+      {appsLoading
         ? <Spinner />
         : <AppGrid
-          apps={props.apps.filter((app: App) => app.isPinned)}
-          totalApps={props.apps.length}
+          apps={apps.filter((app: App) => app.isPinned)}
+          totalApps={apps.length}
         />
       }
 
       <div className={classes.HomeSpace}></div>
 
       <SectionHeadline title='Bookmarks' link='/bookmarks' />
-      {props.categoriesLoading
+      {categoriesLoading
         ? <Spinner />
         : <BookmarkGrid
-            categories={props.categories.filter((category: Category) => category.isPinned)}
-            totalCategories={props.categories.length}
+            categories={categories.filter((category: Category) => category.isPinned)}
+            totalCategories={categories.length}
         />
       }
 

+ 8 - 0
client/src/components/Home/functions/dateTime.ts

@@ -0,0 +1,8 @@
+export const dateTime = (): string => {
+  const days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
+  const months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
+
+  const now = new Date();
+
+  return `${days[now.getDay()]}, ${now.getDate()} ${months[now.getMonth()]} ${now.getFullYear()}`;
+}

+ 12 - 0
client/src/components/Home/functions/greeter.ts

@@ -0,0 +1,12 @@
+export const greeter = (): string => {
+  const now = new Date().getHours();
+  let msg: string;
+
+  if (now >= 18) msg = 'Good evening!';
+  else if (now >= 12) msg = 'Good afternoon!';
+  else if (now >= 6) msg = 'Good morning!';
+  else if (now >= 0) msg = 'Good night!';
+  else msg = 'Hello!';
+
+  return msg;
+}

+ 1 - 1
client/src/components/UI/Modal/Modal.tsx

@@ -1,4 +1,4 @@
-import { MouseEvent, useRef, useEffect } from 'react';
+import { MouseEvent, useRef } from 'react';
 
 import classes from './Modal.module.css';
 

+ 1 - 1
client/src/store/actions/app.ts

@@ -89,7 +89,7 @@ export interface DeleteAppAction {
 
 export const deleteApp = (id: number) => async (dispatch: Dispatch) => {
   try {
-    const res = await axios.delete<ApiResponse<{}>>(`/api/apps/${id}`);
+    await axios.delete<ApiResponse<{}>>(`/api/apps/${id}`);
 
     dispatch<CreateNotificationAction>({
       type: ActionTypes.createNotification,

+ 2 - 2
client/src/store/actions/bookmark.ts

@@ -130,7 +130,7 @@ export interface DeleteCategoryAction {
 
 export const deleteCategory = (id: number) => async (dispatch: Dispatch) => {
   try {
-    const res = await axios.delete<ApiResponse<{}>>(`/api/categories/${id}`);
+    await axios.delete<ApiResponse<{}>>(`/api/categories/${id}`);
 
     dispatch<CreateNotificationAction>({
       type: ActionTypes.createNotification,
@@ -191,7 +191,7 @@ export interface DeleteBookmarkAction {
 
 export const deleteBookmark = (bookmarkId: number, categoryId: number) => async (dispatch: Dispatch) => {
   try {
-    const res = await axios.delete<ApiResponse<{}>>(`/api/bookmarks/${bookmarkId}`);
+    await axios.delete<ApiResponse<{}>>(`/api/bookmarks/${bookmarkId}`);
 
     dispatch<CreateNotificationAction>({
       type: ActionTypes.createNotification,