mirror of
https://github.com/pawelmalak/flame.git
synced 2025-07-19 03:29:37 +02:00
Empty sections will be hidden from guests. Fixed temperature value rounding. Added welcome message
This commit is contained in:
parent
882f011d07
commit
d110d9b732
11 changed files with 80 additions and 71 deletions
|
@ -1,7 +1,9 @@
|
||||||
### v2.0.2 (TBA)
|
### v2.0.2 (TBA)
|
||||||
- Added support for .ico files for custom icons ([#209](https://github.com/pawelmalak/flame/issues/209))
|
- Added support for .ico files for custom icons ([#209](https://github.com/pawelmalak/flame/issues/209))
|
||||||
- Added option to pin apps and categories directly from table view
|
- Empty apps and categories sections will now be hidden from guests ([#210](https://github.com/pawelmalak/flame/issues/210))
|
||||||
|
- Fixed bug with fahrenheit degrees being displayed as float ([#221](https://github.com/pawelmalak/flame/issues/221))
|
||||||
|
- Added option to change visibilty of apps and categories directly from table view
|
||||||
|
- Password input will now autofocus when visiting /settings/app
|
||||||
|
|
||||||
### v2.0.1 (2021-11-19)
|
### v2.0.1 (2021-11-19)
|
||||||
- Added option to display humidity in the weather widget ([#136](https://github.com/pawelmalak/flame/issues/136))
|
- Added option to display humidity in the weather widget ([#136](https://github.com/pawelmalak/flame/issues/136))
|
||||||
|
|
|
@ -20,21 +20,3 @@
|
||||||
grid-template-columns: repeat(4, 1fr);
|
grid-template-columns: repeat(4, 1fr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.GridMessage {
|
|
||||||
color: var(--color-primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.GridMessage a {
|
|
||||||
color: var(--color-accent);
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.AppsMessage {
|
|
||||||
color: var(--color-primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.AppsMessage a {
|
|
||||||
color: var(--color-accent);
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
|
@ -3,6 +3,7 @@ import { Link } from 'react-router-dom';
|
||||||
import { App } from '../../../interfaces/App';
|
import { App } from '../../../interfaces/App';
|
||||||
|
|
||||||
import { AppCard } from '../AppCard/AppCard';
|
import { AppCard } from '../AppCard/AppCard';
|
||||||
|
import { Message } from '../../UI';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
apps: App[];
|
apps: App[];
|
||||||
|
@ -13,36 +14,32 @@ interface Props {
|
||||||
export const AppGrid = (props: Props): JSX.Element => {
|
export const AppGrid = (props: Props): JSX.Element => {
|
||||||
let apps: JSX.Element;
|
let apps: JSX.Element;
|
||||||
|
|
||||||
if (props.apps.length > 0) {
|
if (props.searching || props.apps.length) {
|
||||||
apps = (
|
if (!props.apps.length) {
|
||||||
<div className={classes.AppGrid}>
|
apps = <Message>No apps match your search criteria</Message>;
|
||||||
{props.apps.map((app: App): JSX.Element => {
|
|
||||||
return <AppCard key={app.id} app={app} />;
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
if (props.totalApps) {
|
|
||||||
if (props.searching) {
|
|
||||||
apps = (
|
|
||||||
<p className={classes.AppsMessage}>
|
|
||||||
No apps match your search criteria
|
|
||||||
</p>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
apps = (
|
|
||||||
<p className={classes.AppsMessage}>
|
|
||||||
There are no pinned applications. You can pin them from the{' '}
|
|
||||||
<Link to="/applications">/applications</Link> menu
|
|
||||||
</p>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
apps = (
|
apps = (
|
||||||
<p className={classes.AppsMessage}>
|
<div className={classes.AppGrid}>
|
||||||
|
{props.apps.map((app: App): JSX.Element => {
|
||||||
|
return <AppCard key={app.id} app={app} />;
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (props.totalApps) {
|
||||||
|
apps = (
|
||||||
|
<Message>
|
||||||
|
There are no pinned applications. You can pin them from the{' '}
|
||||||
|
<Link to="/applications">/applications</Link> menu
|
||||||
|
</Message>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
apps = (
|
||||||
|
<Message>
|
||||||
You don't have any applications. You can add a new one from{' '}
|
You don't have any applications. You can add a new one from{' '}
|
||||||
<Link to="/applications">/applications</Link> menu
|
<Link to="/applications">/applications</Link> menu
|
||||||
</p>
|
</Message>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,12 +20,3 @@
|
||||||
grid-template-columns: repeat(4, 1fr);
|
grid-template-columns: repeat(4, 1fr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.BookmarksMessage {
|
|
||||||
color: var(--color-primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.BookmarksMessage a {
|
|
||||||
color: var(--color-accent);
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
|
@ -5,6 +5,7 @@ import classes from './BookmarkGrid.module.css';
|
||||||
import { Category } from '../../../interfaces';
|
import { Category } from '../../../interfaces';
|
||||||
|
|
||||||
import { BookmarkCard } from '../BookmarkCard/BookmarkCard';
|
import { BookmarkCard } from '../BookmarkCard/BookmarkCard';
|
||||||
|
import { Message } from '../../UI';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
categories: Category[];
|
categories: Category[];
|
||||||
|
@ -17,11 +18,7 @@ export const BookmarkGrid = (props: Props): JSX.Element => {
|
||||||
|
|
||||||
if (props.categories.length) {
|
if (props.categories.length) {
|
||||||
if (props.searching && !props.categories[0].bookmarks.length) {
|
if (props.searching && !props.categories[0].bookmarks.length) {
|
||||||
bookmarks = (
|
bookmarks = <Message>No bookmarks match your search criteria</Message>;
|
||||||
<p className={classes.BookmarksMessage}>
|
|
||||||
No bookmarks match your search criteria
|
|
||||||
</p>
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
bookmarks = (
|
bookmarks = (
|
||||||
<div className={classes.BookmarkGrid}>
|
<div className={classes.BookmarkGrid}>
|
||||||
|
@ -36,17 +33,17 @@ export const BookmarkGrid = (props: Props): JSX.Element => {
|
||||||
} else {
|
} else {
|
||||||
if (props.totalCategories) {
|
if (props.totalCategories) {
|
||||||
bookmarks = (
|
bookmarks = (
|
||||||
<p className={classes.BookmarksMessage}>
|
<Message>
|
||||||
There are no pinned categories. You can pin them from the{' '}
|
There are no pinned categories. You can pin them from the{' '}
|
||||||
<Link to="/bookmarks">/bookmarks</Link> menu
|
<Link to="/bookmarks">/bookmarks</Link> menu
|
||||||
</p>
|
</Message>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
bookmarks = (
|
bookmarks = (
|
||||||
<p className={classes.BookmarksMessage}>
|
<Message>
|
||||||
You don't have any bookmarks. You can add a new one from{' '}
|
You don't have any bookmarks. You can add a new one from{' '}
|
||||||
<Link to="/bookmarks">/bookmarks</Link> menu
|
<Link to="/bookmarks">/bookmarks</Link> menu
|
||||||
</p>
|
</Message>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ import { actionCreators } from '../../store';
|
||||||
import { App, Category } from '../../interfaces';
|
import { App, Category } from '../../interfaces';
|
||||||
|
|
||||||
// UI
|
// UI
|
||||||
import { Icon, Container, SectionHeadline, Spinner } from '../UI';
|
import { Icon, Container, SectionHeadline, Spinner, Message } from '../UI';
|
||||||
|
|
||||||
// CSS
|
// CSS
|
||||||
import classes from './Home.module.css';
|
import classes from './Home.module.css';
|
||||||
|
@ -30,6 +30,7 @@ export const Home = (): JSX.Element => {
|
||||||
apps: { apps, loading: appsLoading },
|
apps: { apps, loading: appsLoading },
|
||||||
bookmarks: { categories, loading: bookmarksLoading },
|
bookmarks: { categories, loading: bookmarksLoading },
|
||||||
config: { config },
|
config: { config },
|
||||||
|
auth: { isAuthenticated },
|
||||||
} = useSelector((state: State) => state);
|
} = useSelector((state: State) => state);
|
||||||
|
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
@ -100,7 +101,18 @@ export const Home = (): JSX.Element => {
|
||||||
|
|
||||||
<Header />
|
<Header />
|
||||||
|
|
||||||
{!config.hideApps ? (
|
{!isAuthenticated &&
|
||||||
|
!apps.some((a) => a.isPinned) &&
|
||||||
|
!categories.some((c) => c.isPinned) ? (
|
||||||
|
<Message>
|
||||||
|
Welcome to Flame! Go to <Link to="/settings/app">/settings</Link>,
|
||||||
|
login and start customizing your new homepage
|
||||||
|
</Message>
|
||||||
|
) : (
|
||||||
|
<></>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!config.hideApps && (isAuthenticated || apps.some((a) => a.isPinned)) ? (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<SectionHeadline title="Applications" link="/applications" />
|
<SectionHeadline title="Applications" link="/applications" />
|
||||||
{appsLoading ? (
|
{appsLoading ? (
|
||||||
|
@ -119,10 +131,11 @@ export const Home = (): JSX.Element => {
|
||||||
<div className={classes.HomeSpace}></div>
|
<div className={classes.HomeSpace}></div>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
) : (
|
) : (
|
||||||
<div></div>
|
<></>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!config.hideCategories ? (
|
{!config.hideCategories &&
|
||||||
|
(isAuthenticated || categories.some((c) => c.isPinned)) ? (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<SectionHeadline title="Bookmarks" link="/bookmarks" />
|
<SectionHeadline title="Bookmarks" link="/bookmarks" />
|
||||||
{bookmarksLoading ? (
|
{bookmarksLoading ? (
|
||||||
|
@ -142,7 +155,7 @@ export const Home = (): JSX.Element => {
|
||||||
)}
|
)}
|
||||||
</Fragment>
|
</Fragment>
|
||||||
) : (
|
) : (
|
||||||
<div></div>
|
<></>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Link to="/settings" className={classes.SettingsButton}>
|
<Link to="/settings" className={classes.SettingsButton}>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { FormEvent, Fragment, useEffect, useState } from 'react';
|
import { FormEvent, Fragment, useEffect, useState, useRef } from 'react';
|
||||||
|
|
||||||
// Redux
|
// Redux
|
||||||
import { useSelector, useDispatch } from 'react-redux';
|
import { useSelector, useDispatch } from 'react-redux';
|
||||||
|
@ -23,6 +23,12 @@ export const AuthForm = (): JSX.Element => {
|
||||||
duration: '14d',
|
duration: '14d',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const passwordInputRef = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
passwordInputRef.current?.focus();
|
||||||
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (token) {
|
if (token) {
|
||||||
const decoded = decodeToken(token);
|
const decoded = decodeToken(token);
|
||||||
|
@ -52,6 +58,7 @@ export const AuthForm = (): JSX.Element => {
|
||||||
name="password"
|
name="password"
|
||||||
placeholder="••••••"
|
placeholder="••••••"
|
||||||
autoComplete="current-password"
|
autoComplete="current-password"
|
||||||
|
ref={passwordInputRef}
|
||||||
value={formData.password}
|
value={formData.password}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
setFormData({ ...formData, password: e.target.value })
|
setFormData({ ...formData, password: e.target.value })
|
||||||
|
|
8
client/src/components/UI/Text/Message/Message.module.css
Normal file
8
client/src/components/UI/Text/Message/Message.module.css
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
.message {
|
||||||
|
color: var(--color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.message a {
|
||||||
|
color: var(--color-accent);
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
11
client/src/components/UI/Text/Message/Message.tsx
Normal file
11
client/src/components/UI/Text/Message/Message.tsx
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import { ReactNode } from 'react';
|
||||||
|
|
||||||
|
import classes from './Message.module.css';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
children: ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Message = ({ children }: Props): JSX.Element => {
|
||||||
|
return <p className={classes.message}>{children}</p>;
|
||||||
|
};
|
|
@ -12,3 +12,4 @@ export * from './Forms/InputGroup/InputGroup';
|
||||||
export * from './Forms/ModalForm/ModalForm';
|
export * from './Forms/ModalForm/ModalForm';
|
||||||
export * from './Buttons/ActionButton/ActionButton';
|
export * from './Buttons/ActionButton/ActionButton';
|
||||||
export * from './Buttons/Button/Button';
|
export * from './Buttons/Button/Button';
|
||||||
|
export * from './Text/Message/Message';
|
||||||
|
|
|
@ -71,7 +71,7 @@ export const WeatherWidget = (): JSX.Element => {
|
||||||
{config.isCelsius ? (
|
{config.isCelsius ? (
|
||||||
<span>{weather.tempC}°C</span>
|
<span>{weather.tempC}°C</span>
|
||||||
) : (
|
) : (
|
||||||
<span>{weather.tempF}°F</span>
|
<span>{Math.round(weather.tempF)}°F</span>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* ADDITIONAL DATA */}
|
{/* ADDITIONAL DATA */}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue