Skip to content

Commit 446b409

Browse files
authored
Merge pull request #334 from pawelmalak/feature
Version 2.3.0
2 parents baac780 + 2b5b349 commit 446b409

Some content is hidden

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

66 files changed

+1309
-222
lines changed

.dev/build_dev.sh

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
docker build -t flame:dev -f .docker/Dockerfile .

.dev/build_latest.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
docker build -t pawelmalak/flame -t "pawelmalak/flame:$1" -f .docker/Dockerfile "$2" \
1+
docker build -t pawelmalak/flame -t "pawelmalak/flame:$1" -f .docker/Dockerfile . \
22
&& docker push pawelmalak/flame && docker push "pawelmalak/flame:$1"

.dev/build_multiarch.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@ docker buildx build \
33
-f .docker/Dockerfile.multiarch \
44
-t pawelmalak/flame:multiarch \
55
-t "pawelmalak/flame:multiarch$1" \
6-
--push "$2"
6+
--push .

.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.2.2
3+
VERSION=2.3.0
44
PASSWORD=flame_password
55
SECRET=e02eb43d69953658c6d07311d6313f2d4467672cb881f96b29368ba1f3f4da4b

CHANGELOG.md

+7
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
### v2.3.0 (2022-03-25)
2+
- Added custom theme editor ([#246](https://github.com/pawelmalak/flame/issues/246))
3+
- Added option to set secondary search provider ([#295](https://github.com/pawelmalak/flame/issues/295))
4+
- Fixed bug where pressing Enter with empty search bar would redirect to search results ([#325](https://github.com/pawelmalak/flame/issues/325))
5+
- Fixed bug where user could create empty app or bookmark which was causing page to go blank ([#332](https://github.com/pawelmalak/flame/issues/332))
6+
- Added new theme: Mint
7+
18
### v2.2.2 (2022-03-21)
29
- Added option to get user location directly from the app ([#287](https://github.com/pawelmalak/flame/issues/287))
310
- Fixed bug with local search not working when using prefix ([#289](https://github.com/pawelmalak/flame/issues/289))

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 options 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, 15 built-in color themes and custom theme builder
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

api.js

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ api.use('/api/categories', require('./routes/category'));
2222
api.use('/api/bookmarks', require('./routes/bookmark'));
2323
api.use('/api/queries', require('./routes/queries'));
2424
api.use('/api/auth', require('./routes/auth'));
25+
api.use('/api/themes', require('./routes/themes'));
2526

2627
// Custom error handler
2728
api.use(errorHandler);

client/.env

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
REACT_APP_VERSION=2.2.2
1+
REACT_APP_VERSION=2.3.0

client/src/App.tsx

+7-4
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { actionCreators, store } from './store';
1010
import { State } from './store/reducers';
1111

1212
// Utils
13-
import { checkVersion, decodeToken } from './utility';
13+
import { checkVersion, decodeToken, parsePABToTheme } from './utility';
1414

1515
// Routes
1616
import { Home } from './components/Home/Home';
@@ -31,7 +31,7 @@ export const App = (): JSX.Element => {
3131
const { config, loading } = useSelector((state: State) => state.config);
3232

3333
const dispath = useDispatch();
34-
const { fetchQueries, setTheme, logout, createNotification } =
34+
const { fetchQueries, setTheme, logout, createNotification, fetchThemes } =
3535
bindActionCreators(actionCreators, dispath);
3636

3737
useEffect(() => {
@@ -51,9 +51,12 @@ export const App = (): JSX.Element => {
5151
}
5252
}, 1000);
5353

54+
// load themes
55+
fetchThemes();
56+
5457
// set user theme if present
5558
if (localStorage.theme) {
56-
setTheme(localStorage.theme);
59+
setTheme(parsePABToTheme(localStorage.theme));
5760
}
5861

5962
// check for updated
@@ -68,7 +71,7 @@ export const App = (): JSX.Element => {
6871
// If there is no user theme, set the default one
6972
useEffect(() => {
7073
if (!loading && !localStorage.theme) {
71-
setTheme(config.defaultTheme, false);
74+
setTheme(parsePABToTheme(config.defaultTheme), false);
7275
}
7376
}, [loading]);
7477

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

+13-4
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,8 @@ export const AppForm = ({ modalHandler }: Props): JSX.Element => {
1818
const { appInUpdate } = useSelector((state: State) => state.apps);
1919

2020
const dispatch = useDispatch();
21-
const { addApp, updateApp, setEditApp } = bindActionCreators(
22-
actionCreators,
23-
dispatch
24-
);
21+
const { addApp, updateApp, setEditApp, createNotification } =
22+
bindActionCreators(actionCreators, dispatch);
2523

2624
const [useCustomIcon, toggleUseCustomIcon] = useState<boolean>(false);
2725
const [customIcon, setCustomIcon] = useState<File | null>(null);
@@ -58,6 +56,17 @@ export const AppForm = ({ modalHandler }: Props): JSX.Element => {
5856
const formSubmitHandler = (e: SyntheticEvent<HTMLFormElement>): void => {
5957
e.preventDefault();
6058

59+
for (let field of ['name', 'url', 'icon'] as const) {
60+
if (/^ +$/.test(formData[field])) {
61+
createNotification({
62+
title: 'Error',
63+
message: `Field cannot be empty: ${field}`,
64+
});
65+
66+
return;
67+
}
68+
}
69+
6170
const createFormData = (): FormData => {
6271
const data = new FormData();
6372

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

+11
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,17 @@ export const BookmarksForm = ({
6969
const formSubmitHandler = (e: FormEvent): void => {
7070
e.preventDefault();
7171

72+
for (let field of ['name', 'url', 'icon'] as const) {
73+
if (/^ +$/.test(formData[field])) {
74+
createNotification({
75+
title: 'Error',
76+
message: `Field cannot be empty: ${field}`,
77+
});
78+
79+
return;
80+
}
81+
}
82+
7283
const createFormData = (): FormData => {
7384
const data = new FormData();
7485
if (customIcon) {

client/src/components/SearchBar/SearchBar.tsx

+21-13
Original file line numberDiff line numberDiff line change
@@ -64,16 +64,22 @@ export const SearchBar = (props: Props): JSX.Element => {
6464
};
6565

6666
const searchHandler = (e: KeyboardEvent<HTMLInputElement>) => {
67-
const { isLocal, search, query, isURL, sameTab } = searchParser(
68-
inputRef.current.value
69-
);
67+
const {
68+
isLocal,
69+
encodedURL,
70+
primarySearch,
71+
secondarySearch,
72+
isURL,
73+
sameTab,
74+
rawQuery,
75+
} = searchParser(inputRef.current.value);
7076

7177
if (isLocal) {
72-
setLocalSearch(search);
78+
setLocalSearch(encodedURL);
7379
}
7480

7581
if (e.code === 'Enter' || e.code === 'NumpadEnter') {
76-
if (!query.prefix) {
82+
if (!primarySearch.prefix) {
7783
// Prefix not found -> emit notification
7884
createNotification({
7985
title: 'Error',
@@ -90,19 +96,21 @@ export const SearchBar = (props: Props): JSX.Element => {
9096
} else if (bookmarkSearchResult?.[0]?.bookmarks?.length) {
9197
redirectUrl(bookmarkSearchResult[0].bookmarks[0].url, sameTab);
9298
} else {
93-
// no local results -> search the internet with the default search provider
94-
let template = query.template;
99+
// no local results -> search the internet with the default search provider if query is not empty
100+
if (!/^ *$/.test(rawQuery)) {
101+
let template = primarySearch.template;
95102

96-
if (query.prefix === 'l') {
97-
template = 'https://duckduckgo.com/?q=';
98-
}
103+
if (primarySearch.prefix === 'l') {
104+
template = secondarySearch.template;
105+
}
99106

100-
const url = `${template}${search}`;
101-
redirectUrl(url, sameTab);
107+
const url = `${template}${encodedURL}`;
108+
redirectUrl(url, sameTab);
109+
}
102110
}
103111
} else {
104112
// Valid query -> redirect to search results
105-
const url = `${query.template}${search}`;
113+
const url = `${primarySearch.template}${encodedURL}`;
106114
redirectUrl(url, sameTab);
107115
}
108116
} else if (e.code === 'Escape') {

client/src/components/Settings/GeneralSettings/CustomQueries/CustomQueries.module.css

-30
This file was deleted.

client/src/components/Settings/GeneralSettings/CustomQueries/CustomQueries.tsx

+23-32
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,8 @@ import { actionCreators } from '../../../../store';
99
// Typescript
1010
import { Query } from '../../../../interfaces';
1111

12-
// CSS
13-
import classes from './CustomQueries.module.css';
14-
1512
// UI
16-
import { Modal, Icon, Button } from '../../../UI';
13+
import { Modal, Icon, Button, CompactTable, ActionIcons } from '../../../UI';
1714

1815
// Components
1916
import { QueriesForm } from './QueriesForm';
@@ -67,33 +64,27 @@ export const CustomQueries = (): JSX.Element => {
6764
)}
6865
</Modal>
6966

70-
<div>
71-
<div className={classes.QueriesGrid}>
72-
{customQueries.length > 0 && (
73-
<Fragment>
74-
<span>Name</span>
75-
<span>Prefix</span>
76-
<span>Actions</span>
77-
78-
<div className={classes.Separator}></div>
79-
</Fragment>
80-
)}
81-
82-
{customQueries.map((q: Query, idx) => (
83-
<Fragment key={idx}>
84-
<span>{q.name}</span>
85-
<span>{q.prefix}</span>
86-
<span className={classes.ActionIcons}>
87-
<span onClick={() => updateHandler(q)}>
88-
<Icon icon="mdiPencil" />
89-
</span>
90-
<span onClick={() => deleteHandler(q)}>
91-
<Icon icon="mdiDelete" />
92-
</span>
93-
</span>
94-
</Fragment>
95-
))}
96-
</div>
67+
<section>
68+
{customQueries.length ? (
69+
<CompactTable headers={['Name', 'Prefix', 'Actions']}>
70+
{customQueries.map((q: Query, idx) => (
71+
<Fragment key={idx}>
72+
<span>{q.name}</span>
73+
<span>{q.prefix}</span>
74+
<ActionIcons>
75+
<span onClick={() => updateHandler(q)}>
76+
<Icon icon="mdiPencil" />
77+
</span>
78+
<span onClick={() => deleteHandler(q)}>
79+
<Icon icon="mdiDelete" />
80+
</span>
81+
</ActionIcons>
82+
</Fragment>
83+
))}
84+
</CompactTable>
85+
) : (
86+
<></>
87+
)}
9788

9889
<Button
9990
click={() => {
@@ -103,7 +94,7 @@ export const CustomQueries = (): JSX.Element => {
10394
>
10495
Add new search provider
10596
</Button>
106-
</div>
97+
</section>
10798
</Fragment>
10899
);
109100
};

client/src/components/Settings/GeneralSettings/GeneralSettings.tsx

+30-2
Original file line numberDiff line numberDiff line change
@@ -164,10 +164,10 @@ export const GeneralSettings = (): JSX.Element => {
164164
</select>
165165
</InputGroup>
166166

167-
{/* SEARCH SETTINGS */}
167+
{/* === SEARCH OPTIONS === */}
168168
<SettingsHeadline text="Search" />
169169
<InputGroup>
170-
<label htmlFor="defaultSearchProvider">Default search provider</label>
170+
<label htmlFor="defaultSearchProvider">Primary search provider</label>
171171
<select
172172
id="defaultSearchProvider"
173173
name="defaultSearchProvider"
@@ -186,6 +186,34 @@ export const GeneralSettings = (): JSX.Element => {
186186
</select>
187187
</InputGroup>
188188

189+
{formData.defaultSearchProvider === 'l' && (
190+
<InputGroup>
191+
<label htmlFor="secondarySearchProvider">
192+
Secondary search provider
193+
</label>
194+
<select
195+
id="secondarySearchProvider"
196+
name="secondarySearchProvider"
197+
value={formData.secondarySearchProvider}
198+
onChange={(e) => inputChangeHandler(e)}
199+
>
200+
{[...queries, ...customQueries].map((query: Query, idx) => {
201+
const isCustom = idx >= queries.length;
202+
203+
return (
204+
<option key={idx} value={query.prefix}>
205+
{isCustom && '+'} {query.name}
206+
</option>
207+
);
208+
})}
209+
</select>
210+
<span>
211+
Will be used when "Local search" is primary search provider and
212+
there are not any local results
213+
</span>
214+
</InputGroup>
215+
)}
216+
189217
<InputGroup>
190218
<label htmlFor="searchSameTab">
191219
Open search results in the same tab
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
.ThemeBuilder {
2+
margin-bottom: 30px;
3+
}
4+
5+
.Buttons button:not(:last-child) {
6+
margin-right: 10px;
7+
}

0 commit comments

Comments
 (0)