Added option to set custom greetings. Moved HomeHeader to separate file. Cleaned up README file

This commit is contained in:
Paweł Malak 2021-11-05 16:39:42 +01:00
parent 4e20527834
commit aca8b0261e
17 changed files with 149 additions and 124 deletions

View file

@ -1,5 +1,7 @@
### v1.7.4 (TBA) ### v1.7.4 (TBA)
- [WIP] Added option to set custom greetings and date ([#103](https://github.com/pawelmalak/flame/issues/103))
- Added iOS "Add to homescreen" icon ([#131](https://github.com/pawelmalak/flame/issues/131)) - Added iOS "Add to homescreen" icon ([#131](https://github.com/pawelmalak/flame/issues/131))
- Added 3 new themes
### v1.7.3 (2021-10-28) ### v1.7.3 (2021-10-28)
- Fixed bug with custom CSS not updating - Fixed bug with custom CSS not updating

View file

@ -1,15 +1,10 @@
# Flame # Flame
[![JS Badge](https://img.shields.io/badge/JavaScript-F7DF1E?style=for-the-badge&logo=javascript&logoColor=black)](https://shields.io/)
[![TS Badge](https://img.shields.io/badge/TypeScript-007ACC?style=for-the-badge&logo=typescript&logoColor=white)](https://shields.io/)
[![Node Badge](https://img.shields.io/badge/Node.js-43853D?style=for-the-badge&logo=node.js&logoColor=white)](https://shields.io/)
[![React Badge](https://img.shields.io/badge/React-20232A?style=for-the-badge&logo=react&logoColor=61DAFB)](https://shields.io/)
![Homescreen screenshot](./.github/_home.png) ![Homescreen screenshot](./.github/_home.png)
## Description ## Description
Flame is self-hosted startpage for your server. Its design is inspired (heavily) by [SUI](https://github.com/jeroenpardon/sui). Flame is very easy to setup and use. With built-in editors it allows you to setup your very own appliaction hub in no time - no file editing necessary. Flame is self-hosted startpage for your server. Its design is inspired (heavily) by [SUI](https://github.com/jeroenpardon/sui). Flame is very easy to setup and use. With built-in editors it allows you to setup your very own application hub in no time - no file editing necessary.
## Technology ## Technology
@ -42,7 +37,15 @@ npm run dev
### With Docker (recommended) ### With Docker (recommended)
[Docker Hub](https://hub.docker.com/r/pawelmalak/flame) [Docker Hub link](https://hub.docker.com/r/pawelmalak/flame)
```sh
docker pull pawelmalak/flame:latest
# for ARM architecture (e.g. RaspberryPi)
docker pull pawelmalak/flame:multiarch
```
#### Building images #### Building images
@ -96,13 +99,13 @@ Follow instructions from wiki: [Installation without Docker](https://github.com/
- Applications - Applications
- Create, update, delete and organize applications using GUI - Create, update, delete and organize applications using GUI
- Pin your favourite apps to homescreen - Pin your favourite apps to the homescreen
![Homescreen screenshot](./.github/_apps.png) ![Homescreen screenshot](./.github/_apps.png)
- Bookmarks - Bookmarks
- Create, update, delete and organize bookmarks and categories using GUI - Create, update, delete and organize bookmarks and categories using GUI
- Pin your favourite categories to homescreen - Pin your favourite categories to the homescreen
![Homescreen screenshot](./.github/_bookmarks.png) ![Homescreen screenshot](./.github/_bookmarks.png)
@ -111,7 +114,7 @@ Follow instructions from wiki: [Installation without Docker](https://github.com/
- Get current temperature, cloud coverage and weather status with animated icons - Get current temperature, cloud coverage and weather status with animated icons
- Themes - Themes
- Customize your page by choosing from 12 color themes - Customize your page by choosing from 15 color themes
![Homescreen screenshot](./.github/_themes.png) ![Homescreen screenshot](./.github/_themes.png)
@ -125,23 +128,7 @@ To use search bar you need to type your search query with selected prefix. For e
> You can change where to open search results (same/new tab) in the settings > You can change where to open search results (same/new tab) in the settings
#### Supported search engines For list of supported search engines, shortcuts and more about searching functionality visit [project wiki](https://github.com/pawelmalak/flame/wiki/Search-bar).
| Name | Prefix | Search URL |
| ---------- | ------ | ----------------------------------- |
| Disroot | /ds | http://search.disroot.org/search?q= |
| DuckDuckGo | /d | https://duckduckgo.com/?q= |
| Google | /g | https://www.google.com/search?q= |
#### Supported services
| Name | Prefix | Search URL |
| ------------------ | ------ | --------------------------------------------- |
| IMDb | /im | https://www.imdb.com/find?q= |
| Reddit | /r | https://www.reddit.com/search?q= |
| Spotify | /sp | https://open.spotify.com/search/ |
| The Movie Database | /mv | https://www.themoviedb.org/search?query= |
| Youtube | /yt | https://www.youtube.com/results?search_query= |
### Setting up weather module ### Setting up weather module
@ -159,13 +146,13 @@ labels:
- flame.type=application # "app" works too - flame.type=application # "app" works too
- flame.name=My container - flame.name=My container
- flame.url=https://example.com - flame.url=https://example.com
- flame.icon=icon-name # Optional, default is "docker" - flame.icon=icon-name # optional, default is "docker"
# - flame.icon=custom to make changes in app. ie: custom icon upload # - flame.icon=custom to make changes in app. ie: custom icon upload
``` ```
And you must have activated the Docker sync option in the settings panel. > "Use Docker API" option must be enabled for this to work. You can find it in Settings > Other > Docker section
You can set up different apps in the same label adding `;` between each one. You can also set up different apps in the same label adding `;` between each one.
```yml ```yml
labels: labels:
@ -208,13 +195,11 @@ metadata:
- flame.pawelmalak/type=application # "app" works too - flame.pawelmalak/type=application # "app" works too
- flame.pawelmalak/name=My container - flame.pawelmalak/name=My container
- flame.pawelmalak/url=https://example.com - flame.pawelmalak/url=https://example.com
- flame.pawelmalak/icon=icon-name # Optional, default is "kubernetes" - flame.pawelmalak/icon=icon-name # optional, default is "kubernetes"
``` ```
And you must have activated the Kubernetes sync option in the settings panel. > "Use Kubernetes Ingress API" option must be enabled for this to work. You can find it in Settings > Other > Kubernetes section
### Custom CSS ### Custom CSS and themes
> This is an experimental feature. Its behaviour might change in the future. See project wiki for [Custom CSS](https://github.com/pawelmalak/flame/wiki/Custom-CSS) and [Custom theme with CSS](https://github.com/pawelmalak/flame/wiki/Custom-theme-with-CSS).
>
> Follow instructions from wiki: [Custom CSS](https://github.com/pawelmalak/flame/wiki/Custom-CSS)

View file

@ -0,0 +1,31 @@
.Header h1 {
color: var(--color-primary);
font-weight: 700;
font-size: 4em;
display: inline-block;
}
.Header p {
color: var(--color-primary);
font-weight: 300;
text-transform: uppercase;
height: 30px;
}
.HeaderMain {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 2.5rem;
}
.SettingsLink {
visibility: visible;
color: var(--color-accent);
}
@media (min-width: 769px) {
.SettingsLink {
visibility: hidden;
}
}

View file

@ -0,0 +1,49 @@
import { useEffect, useState } from 'react';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
import { Config, GlobalState } from '../../../interfaces';
import WeatherWidget from '../../Widgets/WeatherWidget/WeatherWidget';
import { getDateTime } from './functions/getDateTime';
import { greeter } from './functions/greeter';
import classes from './Header.module.css';
interface Props {
config: Config;
}
const Header = (props: Props): JSX.Element => {
const [dateTime, setDateTime] = useState<string>(getDateTime());
const [greeting, setGreeting] = useState<string>(greeter());
useEffect(() => {
let dateTimeInterval: NodeJS.Timeout;
dateTimeInterval = setInterval(() => {
setDateTime(getDateTime());
setGreeting(greeter());
}, 1000);
return () => window.clearInterval(dateTimeInterval);
}, []);
return (
<header className={classes.Header}>
<p>{dateTime}</p>
<Link to="/settings" className={classes.SettingsLink}>
Go to Settings
</Link>
<span className={classes.HeaderMain}>
<h1>{greeting}</h1>
<WeatherWidget />
</span>
</header>
);
};
const mapStateToProps = (state: GlobalState) => {
return {
config: state.config.config,
};
};
export default connect(mapStateToProps)(Header);

View file

@ -1,4 +1,4 @@
export const dateTime = (): string => { export const getDateTime = (): string => {
const days = [ const days = [
'Sunday', 'Sunday',
'Monday', 'Monday',

View file

@ -0,0 +1,17 @@
export const greeter = (): string => {
const now = new Date().getHours();
let msg: string;
const greetingsSchemaRaw =
localStorage.getItem('greetingsSchema') ||
'Good evening!;Good afternoon!;Good morning!;Good night!';
const greetingsSchema = greetingsSchemaRaw.split(';');
if (now >= 18) msg = greetingsSchema[0];
else if (now >= 12) msg = greetingsSchema[1];
else if (now >= 6) msg = greetingsSchema[2];
else if (now >= 0) msg = greetingsSchema[3];
else msg = 'Hello!';
return msg;
};

View file

@ -1,24 +1,3 @@
.Header h1 {
color: var(--color-primary);
font-weight: 700;
font-size: 4em;
display: inline-block;
}
.Header p {
color: var(--color-primary);
font-weight: 300;
text-transform: uppercase;
height: 30px;
}
.HeaderMain {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 2.5rem;
}
.SettingsButton { .SettingsButton {
width: 35px; width: 35px;
height: 35px; height: 35px;
@ -40,19 +19,10 @@
opacity: 1; opacity: 1;
} }
.SettingsLink {
visibility: visible;
color: var(--color-accent);
}
@media (min-width: 769px) { @media (min-width: 769px) {
.SettingsButton { .SettingsButton {
visibility: visible; visibility: visible;
} }
.SettingsLink {
visibility: hidden;
}
} }
.HomeSpace { .HomeSpace {

View file

@ -21,12 +21,8 @@ import classes from './Home.module.css';
// Components // Components
import AppGrid from '../Apps/AppGrid/AppGrid'; import AppGrid from '../Apps/AppGrid/AppGrid';
import BookmarkGrid from '../Bookmarks/BookmarkGrid/BookmarkGrid'; import BookmarkGrid from '../Bookmarks/BookmarkGrid/BookmarkGrid';
import WeatherWidget from '../Widgets/WeatherWidget/WeatherWidget';
import SearchBar from '../SearchBar/SearchBar'; import SearchBar from '../SearchBar/SearchBar';
import Header from './Header/Header';
// Functions
import { greeter } from './functions/greeter';
import { dateTime } from './functions/dateTime';
interface ComponentProps { interface ComponentProps {
getApps: Function; getApps: Function;
@ -48,11 +44,6 @@ const Home = (props: ComponentProps): JSX.Element => {
categoriesLoading, categoriesLoading,
} = props; } = props;
const [header, setHeader] = useState({
dateTime: dateTime(),
greeting: greeter(),
});
// Local search query // Local search query
const [localSearch, setLocalSearch] = useState<null | string>(null); const [localSearch, setLocalSearch] = useState<null | string>(null);
const [appSearchResult, setAppSearchResult] = useState<null | App[]>(null); const [appSearchResult, setAppSearchResult] = useState<null | App[]>(null);
@ -74,23 +65,6 @@ const Home = (props: ComponentProps): JSX.Element => {
} }
}, [getCategories]); }, [getCategories]);
// Refresh greeter and time
useEffect(() => {
let interval: any;
// Start interval only when hideHeader is false
if (!props.config.hideHeader) {
interval = setInterval(() => {
setHeader({
dateTime: dateTime(),
greeting: greeter(),
});
}, 1000);
}
return () => clearInterval(interval);
}, []);
useEffect(() => { useEffect(() => {
if (localSearch) { if (localSearch) {
// Search through apps // Search through apps
@ -126,20 +100,7 @@ const Home = (props: ComponentProps): JSX.Element => {
<div></div> <div></div>
)} )}
{!props.config.hideHeader ? ( {!props.config.hideHeader ? <Header /> : <div></div>}
<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>
)}
{!props.config.hideApps ? ( {!props.config.hideApps ? (
<Fragment> <Fragment>

View file

@ -1,12 +0,0 @@
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;
}

View file

@ -187,6 +187,21 @@ const OtherSettings = (props: ComponentProps): JSX.Element => {
<option value={0}>False</option> <option value={0}>False</option>
</select> </select>
</InputGroup> </InputGroup>
<InputGroup>
<label htmlFor="greetingsSchema">Custom greetings</label>
<input
type="text"
id="greetingsSchema"
name="greetingsSchema"
placeholder="Good day;Hi;Bye!"
value={formData.greetingsSchema}
onChange={(e) => inputChangeHandler(e)}
/>
<span>
Greetings must be separated with semicolon. Only 4 messages can be
used
</span>
</InputGroup>
<InputGroup> <InputGroup>
<label htmlFor="hideApps">Hide applications</label> <label htmlFor="hideApps">Hide applications</label>
<select <select

View file

@ -21,4 +21,5 @@ export interface Config {
unpinStoppedApps: boolean; unpinStoppedApps: boolean;
useAmericanDate: boolean; useAmericanDate: boolean;
disableAutofocus: boolean; disableAutofocus: boolean;
greetingsSchema: string;
} }

View file

@ -27,4 +27,5 @@ export interface OtherSettingsForm {
kubernetesApps: boolean; kubernetesApps: boolean;
unpinStoppedApps: boolean; unpinStoppedApps: boolean;
useAmericanDate: boolean; useAmericanDate: boolean;
greetingsSchema: string;
} }

View file

@ -23,6 +23,7 @@ export const getConfig = () => async (dispatch: Dispatch) => {
// Store settings for priority UI elements // Store settings for priority UI elements
localStorage.setItem('useAmericanDate', `${res.data.data.useAmericanDate}`); localStorage.setItem('useAmericanDate', `${res.data.data.useAmericanDate}`);
localStorage.setItem('greetingsSchema', `${res.data.data.greetingsSchema}`);
} catch (err) { } catch (err) {
console.log(err); console.log(err);
} }
@ -52,6 +53,7 @@ export const updateConfig = (formData: any) => async (dispatch: Dispatch) => {
// Store settings for priority UI elements // Store settings for priority UI elements
localStorage.setItem('useAmericanDate', `${res.data.data.useAmericanDate}`); localStorage.setItem('useAmericanDate', `${res.data.data.useAmericanDate}`);
localStorage.setItem('greetingsSchema', `${res.data.data.greetingsSchema}`);
} catch (err) { } catch (err) {
console.log(err); console.log(err);
} }

View file

@ -23,4 +23,5 @@ export const configTemplate: Config = {
unpinStoppedApps: false, unpinStoppedApps: false,
useAmericanDate: false, useAmericanDate: false,
disableAutofocus: false, disableAutofocus: false,
greetingsSchema: 'Good evening!;Good afternoon!;Good morning!;Good night!',
}; };

View file

@ -15,6 +15,7 @@ export const otherSettingsTemplate: OtherSettingsForm = {
kubernetesApps: true, kubernetesApps: true,
unpinStoppedApps: true, unpinStoppedApps: true,
useAmericanDate: false, useAmericanDate: false,
greetingsSchema: 'Good evening!;Good afternoon!;Good morning!;Good night!',
}; };
export const weatherSettingsTemplate: WeatherForm = { export const weatherSettingsTemplate: WeatherForm = {

View file

@ -1,7 +1,7 @@
class ErrorResponse extends Error { class ErrorResponse extends Error {
constructor(message, statusCode) { constructor(message, statusCode) {
super(message); super(message);
this.statusCode = statusCode this.statusCode = statusCode;
} }
} }

View file

@ -20,5 +20,6 @@
"kubernetesApps": false, "kubernetesApps": false,
"unpinStoppedApps": false, "unpinStoppedApps": false,
"useAmericanDate": false, "useAmericanDate": false,
"disableAutofocus": false "disableAutofocus": false,
"greetingsSchema": "Good evening!;Good afternoon!;Good morning!;Good night!"
} }