Skip to content

Commit fd7d8e6

Browse files
authored
Merge pull request #206 from pawelmalak/feature
Version 2.0.1
2 parents 1220c56 + 55b70ee commit fd7d8e6

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+477
-229
lines changed

.env

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
PORT=5005
22
NODE_ENV=development
3-
VERSION=2.0.0
3+
VERSION=2.0.1
44
PASSWORD=flame_password
55
SECRET=e02eb43d69953658c6d07311d6313f2d4467672cb881f96b29368ba1f3f4da4b

CHANGELOG.md

+8
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
### v2.0.1 (2021-11-19)
2+
- Added option to display humidity in the weather widget ([#136](https://github.com/pawelmalak/flame/issues/136))
3+
- Added option to set default theme for all new users ([#165](https://github.com/pawelmalak/flame/issues/165))
4+
- Added option to hide header greetings and date separately ([#200](https://github.com/pawelmalak/flame/issues/200))
5+
- Fixed bug with broken basic auth ([#202](https://github.com/pawelmalak/flame/issues/202))
6+
- Fixed bug with parsing visibility value for apps and bookmarks when custom icon was used ([#203](https://github.com/pawelmalak/flame/issues/203))
7+
- Fixed bug with custom icons not working with apps when "pin by default" was disabled
8+
19
### v2.0.0 (2021-11-15)
210
- Added authentication system:
311
- Only logged in user can access settings ([#33](https://github.com/pawelmalak/flame/issues/33))

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ Flame is self-hosted startpage for your server. Its design is inspired (heavily)
1111
- 📌 Pin your favourite items to the homescreen for quick and easy access
1212
- 🔍 Integrated search bar with local filtering, 11 web search providers and ability to add your own
1313
- 🔑 Authentication system to protect your settings, apps and bookmarks
14-
- 🔨 Dozens of option to customize Flame interface to your needs, including support for custom CSS and 15 built-in color themes
14+
- 🔨 Dozens of options to customize Flame interface to your needs, including support for custom CSS and 15 built-in color themes
1515
- ☀️ Weather widget with current temperature, cloud coverage and animated weather status
1616
- 🐳 Docker integration to automatically pick and add apps based on their labels
1717

client/.env

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
REACT_APP_VERSION=2.0.0
1+
REACT_APP_VERSION=2.0.1

client/src/App.tsx

+17-5
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
1+
import { useEffect } from 'react';
12
import { BrowserRouter, Route, Switch } from 'react-router-dom';
3+
import 'external-svg-loader';
4+
5+
// Redux
6+
import { useDispatch, useSelector } from 'react-redux';
7+
import { bindActionCreators } from 'redux';
28
import { autoLogin, getConfig } from './store/action-creators';
39
import { actionCreators, store } from './store';
4-
import 'external-svg-loader';
10+
import { State } from './store/reducers';
511

612
// Utils
713
import { checkVersion, decodeToken } from './utility';
@@ -12,9 +18,6 @@ import { Apps } from './components/Apps/Apps';
1218
import { Settings } from './components/Settings/Settings';
1319
import { Bookmarks } from './components/Bookmarks/Bookmarks';
1420
import { NotificationCenter } from './components/NotificationCenter/NotificationCenter';
15-
import { useDispatch } from 'react-redux';
16-
import { bindActionCreators } from 'redux';
17-
import { useEffect } from 'react';
1821

1922
// Get config
2023
store.dispatch<any>(getConfig());
@@ -25,6 +28,8 @@ if (localStorage.token) {
2528
}
2629

2730
export const App = (): JSX.Element => {
31+
const { config, loading } = useSelector((state: State) => state.config);
32+
2833
const dispath = useDispatch();
2934
const { fetchQueries, setTheme, logout, createNotification } =
3035
bindActionCreators(actionCreators, dispath);
@@ -46,7 +51,7 @@ export const App = (): JSX.Element => {
4651
}
4752
}, 1000);
4853

49-
// set theme
54+
// set user theme if present
5055
if (localStorage.theme) {
5156
setTheme(localStorage.theme);
5257
}
@@ -60,6 +65,13 @@ export const App = (): JSX.Element => {
6065
return () => window.clearInterval(tokenIsValid);
6166
}, []);
6267

68+
// If there is no user theme, set the default one
69+
useEffect(() => {
70+
if (!loading && !localStorage.theme) {
71+
setTheme(config.defaultTheme, false);
72+
}
73+
}, [loading]);
74+
6375
return (
6476
<>
6577
<BrowserRouter>

client/src/components/Apps/AppForm/AppForm.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ export const AppForm = ({ app, modalHandler }: Props): JSX.Element => {
6161
}
6262
data.append('name', formData.name);
6363
data.append('url', formData.url);
64-
data.append('isPublic', `${formData.isPublic}`);
64+
data.append('isPublic', `${formData.isPublic ? 1 : 0}`);
6565

6666
return data;
6767
};

client/src/components/Bookmarks/Form/BookmarksForm.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ export const BookmarksForm = ({
7777
data.append('name', formData.name);
7878
data.append('url', formData.url);
7979
data.append('categoryId', `${formData.categoryId}`);
80-
data.append('isPublic', `${formData.isPublic}`);
80+
data.append('isPublic', `${formData.isPublic ? 1 : 0}`);
8181

8282
return data;
8383
};

client/src/components/Home/Header/Header.tsx

+17-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
import { useEffect, useState } from 'react';
22
import { Link } from 'react-router-dom';
33

4+
// Redux
5+
import { useSelector } from 'react-redux';
6+
import { State } from '../../../store/reducers';
7+
48
// CSS
59
import classes from './Header.module.css';
610

@@ -12,6 +16,10 @@ import { getDateTime } from './functions/getDateTime';
1216
import { greeter } from './functions/greeter';
1317

1418
export const Header = (): JSX.Element => {
19+
const { hideHeader, hideDate, showTime } = useSelector(
20+
(state: State) => state.config.config
21+
);
22+
1523
const [dateTime, setDateTime] = useState<string>(getDateTime());
1624
const [greeting, setGreeting] = useState<string>(greeter());
1725

@@ -28,14 +36,18 @@ export const Header = (): JSX.Element => {
2836

2937
return (
3038
<header className={classes.Header}>
31-
<p>{dateTime}</p>
39+
{(!hideDate || showTime) && <p>{dateTime}</p>}
40+
3241
<Link to="/settings" className={classes.SettingsLink}>
3342
Go to Settings
3443
</Link>
35-
<span className={classes.HeaderMain}>
36-
<h1>{greeting}</h1>
37-
<WeatherWidget />
38-
</span>
44+
45+
{!hideHeader && (
46+
<span className={classes.HeaderMain}>
47+
<h1>{greeting}</h1>
48+
<WeatherWidget />
49+
</span>
50+
)}
3951
</header>
4052
);
4153
};

client/src/components/Home/Header/functions/getDateTime.ts

+32-12
Original file line numberDiff line numberDiff line change
@@ -30,22 +30,42 @@ export const getDateTime = (): string => {
3030

3131
const useAmericanDate = localStorage.useAmericanDate === 'true';
3232
const showTime = localStorage.showTime === 'true';
33+
const hideDate = localStorage.hideDate === 'true';
3334

35+
// Date
36+
let dateEl = '';
37+
38+
if (!hideDate) {
39+
if (!useAmericanDate) {
40+
dateEl = `${days[now.getDay()]}, ${now.getDate()} ${
41+
months[now.getMonth()]
42+
} ${now.getFullYear()}`;
43+
} else {
44+
dateEl = `${days[now.getDay()]}, ${
45+
months[now.getMonth()]
46+
} ${now.getDate()} ${now.getFullYear()}`;
47+
}
48+
}
49+
50+
// Time
3451
const p = parseTime;
52+
let timeEl = '';
3553

36-
const time = `${p(now.getHours())}:${p(now.getMinutes())}:${p(
37-
now.getSeconds()
38-
)}`;
54+
if (showTime) {
55+
const time = `${p(now.getHours())}:${p(now.getMinutes())}:${p(
56+
now.getSeconds()
57+
)}`;
3958

40-
const timeEl = showTime ? ` - ${time}` : '';
59+
timeEl = time;
60+
}
61+
62+
// Separator
63+
let separator = '';
4164

42-
if (!useAmericanDate) {
43-
return `${days[now.getDay()]}, ${now.getDate()} ${
44-
months[now.getMonth()]
45-
} ${now.getFullYear()}${timeEl}`;
46-
} else {
47-
return `${days[now.getDay()]}, ${
48-
months[now.getMonth()]
49-
} ${now.getDate()} ${now.getFullYear()}${timeEl}`;
65+
if (!hideDate && showTime) {
66+
separator = ' - ';
5067
}
68+
69+
// Output
70+
return `${dateEl}${separator}${timeEl}`;
5171
};

client/src/components/Home/Home.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ export const Home = (): JSX.Element => {
9898
<div></div>
9999
)}
100100

101-
{!config.hideHeader ? <Header /> : <div></div>}
101+
<Header />
102102

103103
{!config.hideApps ? (
104104
<Fragment>

client/src/components/Settings/DockerSettings/DockerSettings.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ export const DockerSettings = (): JSX.Element => {
5959
<SettingsHeadline text="Docker" />
6060
{/* CUSTOM DOCKER SOCKET HOST */}
6161
<InputGroup>
62-
<label htmlFor="dockerHost">Docker Host</label>
62+
<label htmlFor="dockerHost">Docker host</label>
6363
<input
6464
type="text"
6565
id="dockerHost"

client/src/components/Settings/SearchSettings/SearchSettings.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ export const SearchSettings = (): JSX.Element => {
7070
>
7171
<SettingsHeadline text="General" />
7272
<InputGroup>
73-
<label htmlFor="defaultSearchProvider">Default Search Provider</label>
73+
<label htmlFor="defaultSearchProvider">Default search provider</label>
7474
<select
7575
id="defaultSearchProvider"
7676
name="defaultSearchProvider"

client/src/components/Settings/Settings.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { Route as SettingsRoute } from '../../interfaces';
1111
import classes from './Settings.module.css';
1212

1313
// Components
14-
import { Themer } from '../Themer/Themer';
14+
import { Themer } from './Themer/Themer';
1515
import { WeatherSettings } from './WeatherSettings/WeatherSettings';
1616
import { UISettings } from './UISettings/UISettings';
1717
import { AppDetails } from './AppDetails/AppDetails';

client/src/components/Themer/ThemePreview.module.css client/src/components/Settings/Themer/ThemePreview.module.css

+1-2
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
text-transform: capitalize;
1515
margin: 8px 0;
1616
color: var(--color-primary);
17-
/* align-self: flex-start; */
1817
}
1918

2019
.ColorsPreview {
@@ -32,4 +31,4 @@
3231
width: 40px;
3332
height: 40px;
3433
}
35-
}
34+
}

client/src/components/Themer/ThemePreview.tsx client/src/components/Settings/Themer/ThemePreview.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Theme } from '../../interfaces/Theme';
1+
import { Theme } from '../../../interfaces/Theme';
22
import classes from './ThemePreview.module.css';
33

44
interface Props {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import { ChangeEvent, FormEvent, Fragment, useEffect, useState } from 'react';
2+
3+
// Redux
4+
import { useDispatch, useSelector } from 'react-redux';
5+
import { bindActionCreators } from 'redux';
6+
import { actionCreators } from '../../../store';
7+
8+
// Typescript
9+
import { Theme, ThemeSettingsForm } from '../../../interfaces';
10+
11+
// Components
12+
import { ThemePreview } from './ThemePreview';
13+
import { Button, InputGroup, SettingsHeadline } from '../../UI';
14+
15+
// Other
16+
import classes from './Themer.module.css';
17+
import { themes } from './themes.json';
18+
import { State } from '../../../store/reducers';
19+
import { inputHandler, themeSettingsTemplate } from '../../../utility';
20+
21+
export const Themer = (): JSX.Element => {
22+
const {
23+
auth: { isAuthenticated },
24+
config: { loading, config },
25+
} = useSelector((state: State) => state);
26+
27+
const dispatch = useDispatch();
28+
const { setTheme, updateConfig } = bindActionCreators(
29+
actionCreators,
30+
dispatch
31+
);
32+
33+
// Initial state
34+
const [formData, setFormData] = useState<ThemeSettingsForm>(
35+
themeSettingsTemplate
36+
);
37+
38+
// Get config
39+
useEffect(() => {
40+
setFormData({
41+
...config,
42+
});
43+
}, [loading]);
44+
45+
// Form handler
46+
const formSubmitHandler = async (e: FormEvent) => {
47+
e.preventDefault();
48+
49+
// Save settings
50+
await updateConfig(formData);
51+
};
52+
53+
// Input handler
54+
const inputChangeHandler = (
55+
e: ChangeEvent<HTMLInputElement | HTMLSelectElement>,
56+
options?: { isNumber?: boolean; isBool?: boolean }
57+
) => {
58+
inputHandler<ThemeSettingsForm>({
59+
e,
60+
options,
61+
setStateHandler: setFormData,
62+
state: formData,
63+
});
64+
};
65+
66+
return (
67+
<Fragment>
68+
<SettingsHeadline text="Set theme" />
69+
<div className={classes.ThemerGrid}>
70+
{themes.map(
71+
(theme: Theme, idx: number): JSX.Element => (
72+
<ThemePreview key={idx} theme={theme} applyTheme={setTheme} />
73+
)
74+
)}
75+
</div>
76+
77+
{isAuthenticated && (
78+
<form onSubmit={formSubmitHandler}>
79+
<SettingsHeadline text="Other settings" />
80+
<InputGroup>
81+
<label htmlFor="defaultTheme">Default theme (for new users)</label>
82+
<select
83+
id="defaultTheme"
84+
name="defaultTheme"
85+
value={formData.defaultTheme}
86+
onChange={(e) => inputChangeHandler(e)}
87+
>
88+
{themes.map((theme: Theme, idx) => (
89+
<option key={idx} value={theme.name}>
90+
{theme.name}
91+
</option>
92+
))}
93+
</select>
94+
</InputGroup>
95+
96+
<Button>Save changes</Button>
97+
</form>
98+
)}
99+
</Fragment>
100+
);
101+
};

0 commit comments

Comments
 (0)